@blockcast/mmt-container 0.4.1 → 0.5.0-main.34c7b9c2cfa0
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 +2 -2
- package/dist/browser-runtime.js +3769 -364
- package/dist/browser-runtime.js.map +4 -4
- package/dist/cmcd-producer.d.ts +5 -1
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js +18 -2
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/fec-manager.d.ts +21 -16
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +197 -80
- package/dist/fec-manager.js.map +3 -3
- package/dist/held-layer-admission.d.ts +58 -0
- package/dist/held-layer-admission.d.ts.map +1 -0
- package/dist/held-layer-admission.js +105 -0
- package/dist/held-layer-admission.js.map +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mmtp-container-parser.d.ts +10 -0
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +31 -1
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +2713 -1024
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js +31 -1
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-fec-session.d.ts +36 -3
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +114 -5
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +96 -29
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +522 -205
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-auth-catalog.d.ts +10 -2
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +422 -22
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-manager.d.ts +78 -11
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +3663 -333
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +4 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +20 -3
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.d.ts +1 -2
- package/dist/no-modules.d.ts.map +1 -1
- package/dist/no-modules.js +2699 -1008
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.d.ts +50 -10
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +62 -20
- package/dist/repair-fetch.js.map +2 -2
- package/dist/wasm-mmtp-container-parser.js +31 -1
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +7 -15
- package/src/cmcd-producer.ts +6 -1
- package/src/fec-manager.ts +147 -131
- package/src/held-layer-admission.ts +161 -0
- package/src/index.ts +6 -9
- package/src/mmtp-container-parser.ts +48 -0
- package/src/mmtp-fec-session.ts +76 -3
- package/src/mmtp-router.ts +637 -274
- package/src/multicast-auth-catalog.ts +81 -8
- package/src/multicast-manager.ts +274 -49
- package/src/multicast-transport.ts +6 -2
- package/src/no-modules.ts +1 -6
- package/src/repair-fetch.ts +154 -34
- package/test-vectors/catalog/negative/multicast-auth-padded-key.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-root-dotted-key.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +0 -1
- package/test-vectors/repair-policy-v1.json +113 -0
- package/dist/manifest-auth-plan.d.ts +0 -31
- package/dist/manifest-auth-plan.d.ts.map +0 -1
- package/dist/manifest-auth-plan.js +0 -426
- package/dist/manifest-auth-plan.js.map +0 -7
- package/dist/source-auth.d.ts +0 -2
- package/dist/source-auth.d.ts.map +0 -1
- package/dist/source-auth.js +0 -3912
- package/dist/source-auth.js.map +0 -7
- package/src/manifest-auth-plan.ts +0 -55
- package/src/source-auth.ts +0 -1
- package/test-vectors/generate-source-auth-fixture.mjs +0 -114
- package/test-vectors/source-auth-v1.json +0 -61
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { multicastCatalogIssues } from "@blockcast/mmt-base";
|
|
19
|
-
import {
|
|
19
|
+
import { bcProvenanceV2Ctx, type BcProvenanceV2Params, type BcpV2Key } from "@blockcast/mmt-manifest-verify";
|
|
20
20
|
|
|
21
21
|
export type MulticastAuthScheme = "bc-provenance";
|
|
22
22
|
export type MulticastAuthSig = "ed25519";
|
|
@@ -153,11 +153,13 @@ export interface ResolvedMulticastAuthFlow {
|
|
|
153
153
|
manifestTimerMs: number;
|
|
154
154
|
manifestTrack?: string;
|
|
155
155
|
tracks: Array<{ name: string; packetId: number }>;
|
|
156
|
+
/** `ctx` recomputed from the catalog (7.2.4); present when `namespaceFields` was given. */
|
|
157
|
+
ctx?: Uint8Array;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
export interface ResolvedMulticastAuthConfig {
|
|
159
161
|
/** Decoded keys, ascending by `firstEpoch`. */
|
|
160
|
-
keys:
|
|
162
|
+
keys: BcpV2Key[];
|
|
161
163
|
minEpoch: number;
|
|
162
164
|
flows: ResolvedMulticastAuthFlow[];
|
|
163
165
|
}
|
|
@@ -171,10 +173,19 @@ export interface ResolvedMulticastAuthConfig {
|
|
|
171
173
|
* `firstEpoch`, the retired root key) throws, so nothing is admitted and
|
|
172
174
|
* nothing falls back to unauthenticated reception. An endpoint the rules make
|
|
173
175
|
* a receiver ignore yields no flow; the others still bind.
|
|
176
|
+
*
|
|
177
|
+
* `namespaceFields` are the Track Namespace Fields of the catalog track, as
|
|
178
|
+
* MOQT carries them. With them every flow carries the recomputed `ctx`, which
|
|
179
|
+
* the receiver compares against each record; without them `ctx` is carried
|
|
180
|
+
* blind (7.2.4).
|
|
174
181
|
*/
|
|
175
182
|
export function resolveMulticastAuthCatalog(
|
|
176
183
|
catalog: unknown,
|
|
177
|
-
options: {
|
|
184
|
+
options: {
|
|
185
|
+
requireAuth?: boolean;
|
|
186
|
+
catalogNamespace?: string;
|
|
187
|
+
namespaceFields?: readonly (string | Uint8Array)[];
|
|
188
|
+
} = {},
|
|
178
189
|
): ResolvedMulticastAuthConfig | undefined {
|
|
179
190
|
if (typeof catalog !== "object" || catalog === null || Array.isArray(catalog)) {
|
|
180
191
|
throw new Error("[multicast-auth-catalog] catalog must be an object");
|
|
@@ -192,10 +203,22 @@ export function resolveMulticastAuthCatalog(
|
|
|
192
203
|
}
|
|
193
204
|
const ignored = new Set(issues.map((issue) => issue.endpointIndex));
|
|
194
205
|
const endpoints = (multicast!["endpoints"] ?? []) as Array<Record<string, unknown>>;
|
|
206
|
+
const catalogTracks = ((catalog as Record<string, unknown>)["tracks"] ?? []) as Array<Record<string, unknown>>;
|
|
195
207
|
const flows: ResolvedMulticastAuthFlow[] = [];
|
|
196
208
|
endpoints.forEach((endpoint, endpointIndex) => {
|
|
197
209
|
if (ignored.has(endpointIndex)) return;
|
|
198
210
|
const flowAuth = endpoint["auth"] as EndpointAuthConfig;
|
|
211
|
+
const tracks = ((endpoint["tracks"] ?? []) as Array<{ name: string; packetId: number }>).map(({ name, packetId }) => ({
|
|
212
|
+
name,
|
|
213
|
+
packetId,
|
|
214
|
+
}));
|
|
215
|
+
let ctx: Uint8Array | undefined;
|
|
216
|
+
if (options.namespaceFields !== undefined) {
|
|
217
|
+
const params = ctxParams(catalogTracks, tracks);
|
|
218
|
+
// No catalog entry to hash: the endpoint is ignored, nothing is read unauthenticated.
|
|
219
|
+
if (params === undefined) return;
|
|
220
|
+
ctx = bcProvenanceV2Ctx(options.namespaceFields, tracks.map((track) => track.packetId), params);
|
|
221
|
+
}
|
|
199
222
|
flows.push({
|
|
200
223
|
endpointIndex,
|
|
201
224
|
source: (endpoint["sourceAddress"] ?? flowAuth.senderAddress) as string,
|
|
@@ -207,10 +230,8 @@ export function resolveMulticastAuthCatalog(
|
|
|
207
230
|
maxAuthDelayMs: flowAuth.maxAuthDelayMs ?? DEFAULT_MAX_AUTH_DELAY_MS,
|
|
208
231
|
manifestTimerMs: flowAuth.manifestTimerMs ?? DEFAULT_MANIFEST_TIMER_MS,
|
|
209
232
|
...(flowAuth.manifestTrack === undefined ? {} : { manifestTrack: flowAuth.manifestTrack }),
|
|
210
|
-
tracks
|
|
211
|
-
|
|
212
|
-
packetId,
|
|
213
|
-
})),
|
|
233
|
+
tracks,
|
|
234
|
+
...(ctx === undefined ? {} : { ctx }),
|
|
214
235
|
});
|
|
215
236
|
});
|
|
216
237
|
return {
|
|
@@ -222,6 +243,55 @@ export function resolveMulticastAuthCatalog(
|
|
|
222
243
|
};
|
|
223
244
|
}
|
|
224
245
|
|
|
246
|
+
/** A positive integer that fits the u32 it is hashed as (7.2.4), else `0`. */
|
|
247
|
+
function positiveInteger(value: unknown): number {
|
|
248
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= 0xffffffff ? value : 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* `params_hash` inputs (7.2.4): the catalog entry of the endpoint's
|
|
253
|
+
* lowest-`packetId` media track or, for a repair-only endpoint, of the source
|
|
254
|
+
* its lowest-`packetId` repair track repairs (`depends`, else the track whose
|
|
255
|
+
* `fec.repairTrack` names it). A timescale or group duration that does not
|
|
256
|
+
* resolve to an integer tick count is `0`.
|
|
257
|
+
*/
|
|
258
|
+
function ctxParams(
|
|
259
|
+
catalogTracks: Array<Record<string, unknown>>,
|
|
260
|
+
listed: Array<{ name: string; packetId: number }>,
|
|
261
|
+
): BcProvenanceV2Params | undefined {
|
|
262
|
+
const byName = new Map(catalogTracks.map((track) => [track["name"], track]));
|
|
263
|
+
const ordered = [...listed].sort((a, b) => a.packetId - b.packetId).map(({ name }) => byName.get(name));
|
|
264
|
+
let entry = ordered.find((track) => track !== undefined && track["packaging"] !== "fec-repair");
|
|
265
|
+
if (entry === undefined) {
|
|
266
|
+
const repair = ordered.find((track) => track !== undefined);
|
|
267
|
+
if (repair === undefined) return undefined;
|
|
268
|
+
const depends = repair["depends"];
|
|
269
|
+
entry = Array.isArray(depends) && depends.length === 1
|
|
270
|
+
? byName.get(depends[0])
|
|
271
|
+
: catalogTracks.find((track) => (track["fec"] as Record<string, unknown> | undefined)?.["repairTrack"] === repair["name"]);
|
|
272
|
+
if (entry === undefined) return undefined;
|
|
273
|
+
}
|
|
274
|
+
const timescale = positiveInteger(entry["timescale"]);
|
|
275
|
+
const ms = positiveInteger(entry["groupDurationMs"]);
|
|
276
|
+
const groupDurationTicks = positiveInteger(entry["groupDurationTicks"]) ||
|
|
277
|
+
positiveInteger(timescale && (ms * timescale) % 1000 === 0 ? (ms * timescale) / 1000 : 0);
|
|
278
|
+
const fec = entry["fec"] as Record<string, unknown> | undefined;
|
|
279
|
+
return {
|
|
280
|
+
codec: typeof entry["codec"] === "string" ? entry["codec"] : "",
|
|
281
|
+
timescale,
|
|
282
|
+
groupDurationTicks,
|
|
283
|
+
...(fec === undefined || fec === null ? {} : {
|
|
284
|
+
fec: {
|
|
285
|
+
algorithm: typeof fec["algorithm"] === "string" ? fec["algorithm"] : "",
|
|
286
|
+
sourceSymbols: positiveInteger(fec["sourceSymbols"]),
|
|
287
|
+
repairSymbols: positiveInteger(fec["repairSymbols"]),
|
|
288
|
+
symbolSize: positiveInteger(fec["symbolSize"]),
|
|
289
|
+
interleaveDepthMs: positiveInteger(fec["interleaveDepthMs"]),
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
225
295
|
/** The key that signs a record of `kid` at `epoch`, or `undefined` when that
|
|
226
296
|
* `kid` does not sign it (7.2.7: such a record is dropped before verification). */
|
|
227
297
|
export function multicastAuthKeyFor(
|
|
@@ -229,5 +299,8 @@ export function multicastAuthKeyFor(
|
|
|
229
299
|
kid: number,
|
|
230
300
|
epoch: number,
|
|
231
301
|
): Uint8Array | undefined {
|
|
232
|
-
|
|
302
|
+
// `keys` is ascending by firstEpoch with unique kid and firstEpoch (resolve throws otherwise).
|
|
303
|
+
const signers = resolved.keys.filter((key) => key.firstEpoch <= epoch);
|
|
304
|
+
const signer = signers[signers.length - 1];
|
|
305
|
+
return signer?.kid === kid ? signer.publicKey : undefined;
|
|
233
306
|
}
|