@byok-sdk/core 0.21.0 → 0.23.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/dist/index.js CHANGED
@@ -19,6 +19,13 @@ var CORE_ERROR_CODES = {
19
19
  // exactly how a log stops being able to say which authentication surface was
20
20
  // probed.
21
21
  assertion_envelope_invalid: "assertion_envelope_invalid",
22
+ // sealed provider provisioning (plan web-sealed-provisioning). Messages
23
+ // never carry plaintext, ciphertext, or key material — only which check
24
+ // failed.
25
+ webcrypto_unavailable: "webcrypto_unavailable",
26
+ sealed_secret_invalid: "sealed_secret_invalid",
27
+ sealed_secret_open_failed: "sealed_secret_open_failed",
28
+ provider_provisioning_request_invalid: "provider_provisioning_request_invalid",
22
29
  // mailbox (§12.7.3)
23
30
  mailbox_message_not_found: "mailbox_message_not_found",
24
31
  mailbox_cursor_regression: "mailbox_cursor_regression",
@@ -200,8 +207,8 @@ function objectKeyPrefix(value) {
200
207
  return value;
201
208
  }
202
209
  function tenantObjectKey(tenant, hash, prefix) {
203
- const hex = hash.slice("sha256:".length);
204
- const key = `tenants/${tenant}/objects/sha256/${hex}`;
210
+ const hex2 = hash.slice("sha256:".length);
211
+ const key = `tenants/${tenant}/objects/sha256/${hex2}`;
205
212
  return prefix === void 0 ? key : `${prefix}/${key}`;
206
213
  }
207
214
  var OBJECT_STATES = ["pending", "committed", "delete_pending", "deleted"];
@@ -1076,6 +1083,565 @@ async function authenticateTaskAssertion(input, deps) {
1076
1083
  };
1077
1084
  }
1078
1085
 
1086
+ // src/webcrypto.ts
1087
+ function webCryptoSubtle() {
1088
+ const subtle = globalThis.crypto?.subtle;
1089
+ if (subtle === void 0) {
1090
+ throw new ByokCoreError("webcrypto_unavailable", "WebCrypto subtle is unavailable in this runtime");
1091
+ }
1092
+ return subtle;
1093
+ }
1094
+
1095
+ // src/hpke.ts
1096
+ var KEM_ID = 16;
1097
+ var KDF_ID = 1;
1098
+ var AEAD_ID = 1;
1099
+ var HPKE_P256_PUBLIC_KEY_BYTES = 65;
1100
+ var N_SECRET = 32;
1101
+ var N_K = 16;
1102
+ var N_N = 12;
1103
+ var N_H = 32;
1104
+ var HPKE_AEAD_TAG_BYTES = 16;
1105
+ var MODE_BASE = 0;
1106
+ var HPKE_V1 = utf8("HPKE-v1");
1107
+ var KEM_SUITE_ID = concat(utf8("KEM"), i2osp(KEM_ID, 2));
1108
+ var HPKE_SUITE_ID = concat(utf8("HPKE"), i2osp(KEM_ID, 2), i2osp(KDF_ID, 2), i2osp(AEAD_ID, 2));
1109
+ var EMPTY = new Uint8Array(0);
1110
+ var ZERO_SALT = new Uint8Array(N_H);
1111
+ function utf8(value) {
1112
+ return new TextEncoder().encode(value);
1113
+ }
1114
+ function concat(...parts) {
1115
+ const length = parts.reduce((total, part) => total + part.byteLength, 0);
1116
+ const out = new Uint8Array(length);
1117
+ let offset = 0;
1118
+ for (const part of parts) {
1119
+ out.set(part, offset);
1120
+ offset += part.byteLength;
1121
+ }
1122
+ return out;
1123
+ }
1124
+ function i2osp(value, length) {
1125
+ if (!Number.isSafeInteger(value) || value < 0) {
1126
+ throw new ByokCoreError("sealed_secret_invalid", "HPKE integer encoding requires a non-negative safe integer");
1127
+ }
1128
+ const out = new Uint8Array(length);
1129
+ let remaining = value;
1130
+ for (let index = length - 1; index >= 0; index -= 1) {
1131
+ out[index] = remaining % 256;
1132
+ remaining = Math.floor(remaining / 256);
1133
+ }
1134
+ if (remaining !== 0) {
1135
+ throw new ByokCoreError("sealed_secret_invalid", "HPKE integer does not fit the requested width");
1136
+ }
1137
+ return out;
1138
+ }
1139
+ function copy(bytes) {
1140
+ return new Uint8Array(bytes);
1141
+ }
1142
+ async function hmacSha256(key, data) {
1143
+ const subtle = webCryptoSubtle();
1144
+ const hmacKey = await subtle.importKey("raw", copy(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
1145
+ return new Uint8Array(await subtle.sign("HMAC", hmacKey, copy(data)));
1146
+ }
1147
+ async function hkdfExpand(prk, info, length) {
1148
+ if (length > 255 * N_H) {
1149
+ throw new ByokCoreError("sealed_secret_invalid", "HKDF output length exceeds its limit");
1150
+ }
1151
+ const out = new Uint8Array(length);
1152
+ let previous = EMPTY;
1153
+ let offset = 0;
1154
+ for (let counter = 1; offset < length; counter += 1) {
1155
+ previous = await hmacSha256(prk, concat(previous, info, Uint8Array.of(counter)));
1156
+ const take = Math.min(previous.byteLength, length - offset);
1157
+ out.set(previous.subarray(0, take), offset);
1158
+ offset += take;
1159
+ }
1160
+ return out;
1161
+ }
1162
+ async function labeledExtract(suiteId, salt, label, ikm) {
1163
+ return hmacSha256(salt.byteLength === 0 ? ZERO_SALT : salt, concat(HPKE_V1, suiteId, utf8(label), ikm));
1164
+ }
1165
+ async function labeledExpand(suiteId, prk, label, info, length) {
1166
+ return hkdfExpand(prk, concat(i2osp(length, 2), HPKE_V1, suiteId, utf8(label), info), length);
1167
+ }
1168
+ async function importP256PublicKey(raw) {
1169
+ if (raw.byteLength !== HPKE_P256_PUBLIC_KEY_BYTES || raw[0] !== 4) {
1170
+ throw new ByokCoreError("sealed_secret_invalid", "P-256 public key must be a 65-byte uncompressed point");
1171
+ }
1172
+ try {
1173
+ return await webCryptoSubtle().importKey("raw", copy(raw), { name: "ECDH", namedCurve: "P-256" }, true, []);
1174
+ } catch (cause) {
1175
+ throw new ByokCoreError("sealed_secret_invalid", "P-256 public key is not a valid curve point", { cause });
1176
+ }
1177
+ }
1178
+ async function ecdhP256(privateKey, publicKey) {
1179
+ const bits = await webCryptoSubtle().deriveBits({ name: "ECDH", public: publicKey }, privateKey, N_SECRET * 8);
1180
+ return new Uint8Array(bits);
1181
+ }
1182
+ async function extractAndExpand(dh, kemContext) {
1183
+ const eaePrk = await labeledExtract(KEM_SUITE_ID, EMPTY, "eae_prk", dh);
1184
+ return labeledExpand(KEM_SUITE_ID, eaePrk, "shared_secret", kemContext, N_SECRET);
1185
+ }
1186
+ async function keyScheduleBase(sharedSecret, info) {
1187
+ const pskIdHash = await labeledExtract(HPKE_SUITE_ID, EMPTY, "psk_id_hash", EMPTY);
1188
+ const infoHash = await labeledExtract(HPKE_SUITE_ID, EMPTY, "info_hash", info);
1189
+ const context = concat(Uint8Array.of(MODE_BASE), pskIdHash, infoHash);
1190
+ const secret = await labeledExtract(HPKE_SUITE_ID, sharedSecret, "secret", EMPTY);
1191
+ return {
1192
+ sharedSecret,
1193
+ key: await labeledExpand(HPKE_SUITE_ID, secret, "key", context, N_K),
1194
+ baseNonce: await labeledExpand(HPKE_SUITE_ID, secret, "base_nonce", context, N_N),
1195
+ exporterSecret: await labeledExpand(HPKE_SUITE_ID, secret, "exp", context, N_H)
1196
+ };
1197
+ }
1198
+ async function setupBaseSender(recipientPublicKey, info, ephemeral) {
1199
+ const pkR = await importP256PublicKey(recipientPublicKey);
1200
+ let pair = ephemeral;
1201
+ if (pair === void 0) {
1202
+ const subtle = webCryptoSubtle();
1203
+ const generated = await subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]);
1204
+ pair = {
1205
+ privateKey: generated.privateKey,
1206
+ publicKey: new Uint8Array(await subtle.exportKey("raw", generated.publicKey))
1207
+ };
1208
+ }
1209
+ const enc = copy(pair.publicKey);
1210
+ const dh = await ecdhP256(pair.privateKey, pkR);
1211
+ const sharedSecret = await extractAndExpand(dh, concat(enc, recipientPublicKey));
1212
+ return { enc, schedule: await keyScheduleBase(sharedSecret, info) };
1213
+ }
1214
+ async function setupBaseRecipient(enc, recipientPrivateKey, recipientPublicKey, info) {
1215
+ if (recipientPublicKey.byteLength !== HPKE_P256_PUBLIC_KEY_BYTES || recipientPublicKey[0] !== 4) {
1216
+ throw new ByokCoreError("sealed_secret_invalid", "P-256 recipient public key must be a 65-byte uncompressed point");
1217
+ }
1218
+ const pkE = await importP256PublicKey(enc);
1219
+ let dh;
1220
+ try {
1221
+ dh = await ecdhP256(recipientPrivateKey, pkE);
1222
+ } catch (cause) {
1223
+ throw new ByokCoreError("sealed_secret_open_failed", "HPKE decapsulation failed", { cause });
1224
+ }
1225
+ const sharedSecret = await extractAndExpand(dh, concat(enc, recipientPublicKey));
1226
+ return keyScheduleBase(sharedSecret, info);
1227
+ }
1228
+ function hpkeNonce(baseNonce, sequence) {
1229
+ const seq = i2osp(sequence, N_N);
1230
+ const out = new Uint8Array(N_N);
1231
+ for (let index = 0; index < N_N; index += 1) out[index] = baseNonce[index] ^ seq[index];
1232
+ return out;
1233
+ }
1234
+ async function aesGcmKey(key, usage) {
1235
+ return webCryptoSubtle().importKey("raw", copy(key), { name: "AES-GCM" }, false, [usage]);
1236
+ }
1237
+ async function hpkeAeadSeal(schedule, sequence, aad, plaintext) {
1238
+ const ciphertext = await webCryptoSubtle().encrypt(
1239
+ { name: "AES-GCM", iv: hpkeNonce(schedule.baseNonce, sequence), additionalData: copy(aad), tagLength: 128 },
1240
+ await aesGcmKey(schedule.key, "encrypt"),
1241
+ copy(plaintext)
1242
+ );
1243
+ return new Uint8Array(ciphertext);
1244
+ }
1245
+ async function hpkeAeadOpen(schedule, sequence, aad, ciphertext) {
1246
+ if (ciphertext.byteLength < HPKE_AEAD_TAG_BYTES) {
1247
+ throw new ByokCoreError("sealed_secret_invalid", "HPKE ciphertext is shorter than its authentication tag");
1248
+ }
1249
+ try {
1250
+ const plaintext = await webCryptoSubtle().decrypt(
1251
+ { name: "AES-GCM", iv: hpkeNonce(schedule.baseNonce, sequence), additionalData: copy(aad), tagLength: 128 },
1252
+ await aesGcmKey(schedule.key, "decrypt"),
1253
+ copy(ciphertext)
1254
+ );
1255
+ return new Uint8Array(plaintext);
1256
+ } catch (cause) {
1257
+ throw new ByokCoreError("sealed_secret_open_failed", "HPKE ciphertext failed authentication", { cause });
1258
+ }
1259
+ }
1260
+ async function hpkeSealBase(input) {
1261
+ const { enc, schedule } = await setupBaseSender(input.recipientPublicKey, input.info);
1262
+ return { enc, ciphertext: await hpkeAeadSeal(schedule, 0, input.aad, input.plaintext) };
1263
+ }
1264
+ async function hpkeOpenBase(input) {
1265
+ const schedule = await setupBaseRecipient(input.enc, input.recipientPrivateKey, input.recipientPublicKey, input.info);
1266
+ return hpkeAeadOpen(schedule, 0, input.aad, input.ciphertext);
1267
+ }
1268
+
1269
+ // src/sealed-provider-secret.ts
1270
+ var PROVIDER_SECRET_HPKE_SUITE = "hpke-base.dhkem-p256-hkdf-sha256.hkdf-sha256.aes-128-gcm";
1271
+ var PROVIDER_SECRET_HPKE_INFO = "byok-sdk.provider-secret.v1";
1272
+ var PROVIDER_PROVISIONING_REQUEST_VERSION = 1;
1273
+ var PROVIDER_PROVISIONING_OPERATIONS = ["configure", "update_model", "replace_secret", "delete"];
1274
+ var PROVIDER_PROVISIONING_SECRET_OPERATIONS = ["configure", "replace_secret"];
1275
+ var PROVIDER_PROVISIONING_MAX_TTL_MS = 15 * 60 * 1e3;
1276
+ var PROVIDER_SECRET_PAD_BLOCK_BYTES = 256;
1277
+ var PROVIDER_SECRET_MAX_PADDED_BYTES = 4096;
1278
+ var PROVIDER_SECRET_MAX_BYTES = PROVIDER_SECRET_MAX_PADDED_BYTES - 2;
1279
+ var SEALING_KEY_ID_LENGTH = 22;
1280
+ var B64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1281
+ var B64URL_PATTERN = /^[A-Za-z0-9_-]*$/;
1282
+ function encodeBase64Url(bytes) {
1283
+ let out = "";
1284
+ let index = 0;
1285
+ for (; index + 3 <= bytes.length; index += 3) {
1286
+ const value = bytes[index] << 16 | bytes[index + 1] << 8 | bytes[index + 2];
1287
+ out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63] + B64URL_ALPHABET[value >> 6 & 63] + B64URL_ALPHABET[value & 63];
1288
+ }
1289
+ const rest = bytes.length - index;
1290
+ if (rest === 1) {
1291
+ const value = bytes[index] << 16;
1292
+ out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63];
1293
+ } else if (rest === 2) {
1294
+ const value = bytes[index] << 16 | bytes[index + 1] << 8;
1295
+ out += B64URL_ALPHABET[value >> 18 & 63] + B64URL_ALPHABET[value >> 12 & 63] + B64URL_ALPHABET[value >> 6 & 63];
1296
+ }
1297
+ return out;
1298
+ }
1299
+ function decodeBase64Url(value, field) {
1300
+ if (!B64URL_PATTERN.test(value) || value.length % 4 === 1) {
1301
+ throw new ByokCoreError("sealed_secret_invalid", `${field} is not canonical base64url`);
1302
+ }
1303
+ const out = new Uint8Array(Math.floor(value.length * 3 / 4));
1304
+ let buffer = 0;
1305
+ let bits = 0;
1306
+ let offset = 0;
1307
+ for (const char of value) {
1308
+ buffer = buffer << 6 | B64URL_ALPHABET.indexOf(char);
1309
+ bits += 6;
1310
+ if (bits >= 8) {
1311
+ bits -= 8;
1312
+ out[offset] = buffer >> bits & 255;
1313
+ offset += 1;
1314
+ }
1315
+ }
1316
+ if (encodeBase64Url(out) !== value) {
1317
+ throw new ByokCoreError("sealed_secret_invalid", `${field} is not canonical base64url`);
1318
+ }
1319
+ return out;
1320
+ }
1321
+ function base64UrlField(bytes) {
1322
+ return z.string().superRefine((value, ctx) => {
1323
+ try {
1324
+ const decoded = decodeBase64Url(value, "field");
1325
+ if (bytes.exact !== void 0 && decoded.byteLength !== bytes.exact) {
1326
+ ctx.addIssue({ code: "custom", message: `must decode to exactly ${bytes.exact} bytes` });
1327
+ }
1328
+ if (bytes.max !== void 0 && decoded.byteLength > bytes.max) {
1329
+ ctx.addIssue({ code: "custom", message: `must decode to at most ${bytes.max} bytes` });
1330
+ }
1331
+ } catch {
1332
+ ctx.addIssue({ code: "custom", message: "must be canonical unpadded base64url" });
1333
+ }
1334
+ });
1335
+ }
1336
+ async function sha256(bytes) {
1337
+ return new Uint8Array(await webCryptoSubtle().digest("SHA-256", new Uint8Array(bytes)));
1338
+ }
1339
+ function hex(bytes) {
1340
+ let out = "";
1341
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
1342
+ return out;
1343
+ }
1344
+ async function contentDigest(value) {
1345
+ return `sha256:${hex(await sha256(canonicalizeJsonBytes(value)))}`;
1346
+ }
1347
+ var SealingPublicJwkSchema = z.strictObject({
1348
+ kty: z.literal("EC"),
1349
+ crv: z.literal("P-256"),
1350
+ x: base64UrlField({ exact: 32 }),
1351
+ y: base64UrlField({ exact: 32 })
1352
+ });
1353
+ var SealingKeyIdSchema = z.string().regex(new RegExp(`^[A-Za-z0-9_-]{${SEALING_KEY_ID_LENGTH}}$`));
1354
+ function parseSealingPublicJwk(input) {
1355
+ const result = SealingPublicJwkSchema.safeParse(input);
1356
+ if (!result.success) {
1357
+ throw new ByokCoreError("sealed_secret_invalid", "Sealing public key must be a strict P-256 EC public JWK");
1358
+ }
1359
+ return result.data;
1360
+ }
1361
+ function sealingPublicKeyBytes(publicJwk) {
1362
+ const jwk = parseSealingPublicJwk(publicJwk);
1363
+ const raw = new Uint8Array(HPKE_P256_PUBLIC_KEY_BYTES);
1364
+ raw[0] = 4;
1365
+ raw.set(decodeBase64Url(jwk.x, "x"), 1);
1366
+ raw.set(decodeBase64Url(jwk.y, "y"), 33);
1367
+ return raw;
1368
+ }
1369
+ async function deriveSealingKeyId(publicJwk) {
1370
+ const raw = sealingPublicKeyBytes(publicJwk);
1371
+ await importP256PublicKey(raw);
1372
+ return encodeBase64Url(await sha256(raw)).slice(0, SEALING_KEY_ID_LENGTH);
1373
+ }
1374
+ var SealingKeyClaimV1Schema = z.strictObject({
1375
+ version: z.literal(1),
1376
+ keyId: SealingKeyIdSchema,
1377
+ epoch: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1378
+ publicJwk: SealingPublicJwkSchema
1379
+ });
1380
+ function sealingKeyClaimCanonicalBytes(claim) {
1381
+ const result = SealingKeyClaimV1Schema.safeParse(claim);
1382
+ if (!result.success) {
1383
+ throw new ByokCoreError("sealed_secret_invalid", "Sealing key claim is invalid");
1384
+ }
1385
+ const parsed = result.data;
1386
+ return canonicalizeJsonBytes({
1387
+ version: parsed.version,
1388
+ keyId: parsed.keyId,
1389
+ epoch: parsed.epoch,
1390
+ publicJwk: { kty: parsed.publicJwk.kty, crv: parsed.publicJwk.crv, x: parsed.publicJwk.x, y: parsed.publicJwk.y }
1391
+ });
1392
+ }
1393
+ var OpaqueIdSchema = z.string().min(1).max(200).superRefine((value, ctx) => {
1394
+ if (value.trim() !== value || /[\u0000-\u001f\u007f]/u.test(value)) {
1395
+ ctx.addIssue({ code: "custom", message: "must not carry surrounding whitespace or control characters" });
1396
+ }
1397
+ });
1398
+ var CanonicalTimestampSchema = z.string().refine(isCanonicalTimestamp, "must be a canonical ISO-8601 UTC instant");
1399
+ var ProfileRefSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);
1400
+ var ProviderKindSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/u);
1401
+ var ModelIdSchema = z.string().min(1).max(160).refine((value) => value.trim() === value && !/[\u0000-\u001f\u007f]/u.test(value), "model id is invalid");
1402
+ var CapabilitySchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u);
1403
+ var PiModelJsonSchema = z.custom((value) => {
1404
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1405
+ try {
1406
+ canonicalizeJson(value);
1407
+ return true;
1408
+ } catch {
1409
+ return false;
1410
+ }
1411
+ }, "piModel must be a canonical JSON object");
1412
+ var ModelConfigFields = {
1413
+ agentId: OpaqueIdSchema,
1414
+ providerKind: ProviderKindSchema,
1415
+ modelId: ModelIdSchema,
1416
+ piModel: PiModelJsonSchema,
1417
+ capabilities: z.array(CapabilitySchema).max(8).refine((values) => new Set(values).size === values.length, "capabilities must be unique")
1418
+ };
1419
+ var ProviderProvisioningConfigV1Schema = z.discriminatedUnion("operation", [
1420
+ z.strictObject({ operation: z.literal("configure"), ...ModelConfigFields }),
1421
+ z.strictObject({ operation: z.literal("update_model"), ...ModelConfigFields }),
1422
+ z.strictObject({ operation: z.literal("replace_secret"), agentId: OpaqueIdSchema, providerKind: ProviderKindSchema }),
1423
+ z.strictObject({ operation: z.literal("delete"), agentId: OpaqueIdSchema })
1424
+ ]);
1425
+ var ProviderProvisioningExpectedProfileSchema = z.union([
1426
+ z.null(),
1427
+ z.strictObject({
1428
+ profileRef: ProfileRefSchema,
1429
+ profileRevision: z.string().regex(/^(?:0|[1-9][0-9]{0,19})$/u),
1430
+ profileHash: z.string().regex(CONTENT_HASH_PATTERN)
1431
+ })
1432
+ ]);
1433
+ var ProviderProvisioningIdentityRevisionSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/u);
1434
+ var ProviderProvisioningHeaderV1Schema = z.strictObject({
1435
+ tenantId: OpaqueIdSchema,
1436
+ deviceId: OpaqueIdSchema,
1437
+ agentId: OpaqueIdSchema,
1438
+ requestId: OpaqueIdSchema,
1439
+ operation: z.enum(PROVIDER_PROVISIONING_OPERATIONS),
1440
+ /** Non-secret, strictly increasing per bot (A3); a device rejects anything not above its high-watermark. */
1441
+ operationGeneration: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1442
+ expectedProfile: ProviderProvisioningExpectedProfileSchema,
1443
+ /** The device enrollment revision the Host authorized against (F2). */
1444
+ expectedEnrollmentRevision: ProviderProvisioningIdentityRevisionSchema,
1445
+ /** The agent placement revision the Host authorized against (F2). */
1446
+ expectedPlacementRevision: ProviderProvisioningIdentityRevisionSchema,
1447
+ configDigest: z.string().regex(CONTENT_HASH_PATTERN),
1448
+ issuedAt: CanonicalTimestampSchema,
1449
+ expiresAt: CanonicalTimestampSchema
1450
+ });
1451
+ var MAX_CIPHERTEXT_BYTES = PROVIDER_SECRET_MAX_PADDED_BYTES + HPKE_AEAD_TAG_BYTES;
1452
+ var SealedProviderSecretV1Schema = z.strictObject({
1453
+ suite: z.literal(PROVIDER_SECRET_HPKE_SUITE),
1454
+ keyId: SealingKeyIdSchema,
1455
+ enc: base64UrlField({ exact: HPKE_P256_PUBLIC_KEY_BYTES }),
1456
+ ciphertext: base64UrlField({ max: MAX_CIPHERTEXT_BYTES })
1457
+ });
1458
+ function isSecretOperation(operation) {
1459
+ return PROVIDER_PROVISIONING_SECRET_OPERATIONS.includes(operation);
1460
+ }
1461
+ var ProviderProvisioningRequestV1Schema = z.strictObject({
1462
+ version: z.literal(PROVIDER_PROVISIONING_REQUEST_VERSION),
1463
+ header: ProviderProvisioningHeaderV1Schema,
1464
+ config: ProviderProvisioningConfigV1Schema,
1465
+ sealed: SealedProviderSecretV1Schema.optional()
1466
+ }).superRefine((request, ctx) => {
1467
+ if (request.header.operation !== request.config.operation) {
1468
+ ctx.addIssue({ code: "custom", path: ["config", "operation"], message: "config operation must equal header operation" });
1469
+ }
1470
+ if (request.header.agentId !== request.config.agentId) {
1471
+ ctx.addIssue({ code: "custom", path: ["config", "agentId"], message: "config agentId must equal header agentId" });
1472
+ }
1473
+ if (isSecretOperation(request.header.operation) !== (request.sealed !== void 0)) {
1474
+ ctx.addIssue({ code: "custom", path: ["sealed"], message: "a sealed secret is required exactly for configure and replace_secret" });
1475
+ }
1476
+ });
1477
+ function parseProviderProvisioningRequest(input) {
1478
+ const result = ProviderProvisioningRequestV1Schema.safeParse(input);
1479
+ if (!result.success) {
1480
+ throw new ByokCoreError(
1481
+ "provider_provisioning_request_invalid",
1482
+ `Invalid provider provisioning request: ${result.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`
1483
+ );
1484
+ }
1485
+ return result.data;
1486
+ }
1487
+ function configJson(config) {
1488
+ return config;
1489
+ }
1490
+ async function providerProvisioningConfigDigest(config) {
1491
+ const result = ProviderProvisioningConfigV1Schema.safeParse(config);
1492
+ if (!result.success) {
1493
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config is invalid");
1494
+ }
1495
+ return contentDigest(configJson(result.data));
1496
+ }
1497
+ function providerSecretAadBytes(header, keyId) {
1498
+ const parsed = ProviderProvisioningHeaderV1Schema.safeParse(header);
1499
+ if (!parsed.success || !SealingKeyIdSchema.safeParse(keyId).success) {
1500
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning AAD input is invalid");
1501
+ }
1502
+ const value = parsed.data;
1503
+ return canonicalizeJsonBytes({
1504
+ v: PROVIDER_PROVISIONING_REQUEST_VERSION,
1505
+ tenantId: value.tenantId,
1506
+ deviceId: value.deviceId,
1507
+ keyId,
1508
+ requestId: value.requestId,
1509
+ agentId: value.agentId,
1510
+ operation: value.operation,
1511
+ operationGeneration: value.operationGeneration,
1512
+ expectedProfile: value.expectedProfile === null ? null : {
1513
+ profileRef: value.expectedProfile.profileRef,
1514
+ profileRevision: value.expectedProfile.profileRevision,
1515
+ profileHash: value.expectedProfile.profileHash
1516
+ },
1517
+ expectedEnrollmentRevision: value.expectedEnrollmentRevision,
1518
+ expectedPlacementRevision: value.expectedPlacementRevision,
1519
+ configDigest: value.configDigest,
1520
+ issuedAt: value.issuedAt,
1521
+ expiresAt: value.expiresAt
1522
+ });
1523
+ }
1524
+ async function providerProvisioningRequestDigest(request) {
1525
+ const parsed = parseProviderProvisioningRequest(request);
1526
+ return contentDigest(parsed);
1527
+ }
1528
+ async function assertProviderProvisioningConfigDigest(request) {
1529
+ const parsed = parseProviderProvisioningRequest(request);
1530
+ if (await providerProvisioningConfigDigest(parsed.config) !== parsed.header.configDigest) {
1531
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config digest does not match its header");
1532
+ }
1533
+ }
1534
+ function padProviderSecret(secret) {
1535
+ const bytes = new TextEncoder().encode(secret);
1536
+ if (bytes.byteLength === 0 || bytes.byteLength > PROVIDER_SECRET_MAX_BYTES) {
1537
+ throw new ByokCoreError("sealed_secret_invalid", `Provider secret must be 1 to ${PROVIDER_SECRET_MAX_BYTES} UTF-8 bytes`);
1538
+ }
1539
+ const total = Math.ceil((bytes.byteLength + 2) / PROVIDER_SECRET_PAD_BLOCK_BYTES) * PROVIDER_SECRET_PAD_BLOCK_BYTES;
1540
+ const out = new Uint8Array(total);
1541
+ out[0] = bytes.byteLength >> 8;
1542
+ out[1] = bytes.byteLength & 255;
1543
+ out.set(bytes, 2);
1544
+ return out;
1545
+ }
1546
+ function unpadProviderSecret(padded) {
1547
+ if (padded.byteLength < PROVIDER_SECRET_PAD_BLOCK_BYTES || padded.byteLength > PROVIDER_SECRET_MAX_PADDED_BYTES || padded.byteLength % PROVIDER_SECRET_PAD_BLOCK_BYTES !== 0) {
1548
+ throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has an invalid length");
1549
+ }
1550
+ const length = padded[0] << 8 | padded[1];
1551
+ if (length === 0 || length + 2 > padded.byteLength || padded.byteLength - (length + 2) >= PROVIDER_SECRET_PAD_BLOCK_BYTES) {
1552
+ throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has an invalid length prefix");
1553
+ }
1554
+ for (let index = length + 2; index < padded.byteLength; index += 1) {
1555
+ if (padded[index] !== 0) {
1556
+ throw new ByokCoreError("sealed_secret_invalid", "Padded provider secret has non-zero padding");
1557
+ }
1558
+ }
1559
+ try {
1560
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(padded.subarray(2, length + 2));
1561
+ } catch {
1562
+ throw new ByokCoreError("sealed_secret_invalid", "Provider secret is not valid UTF-8");
1563
+ }
1564
+ }
1565
+ var INFO_BYTES = () => new TextEncoder().encode(PROVIDER_SECRET_HPKE_INFO);
1566
+ async function sealProviderProvisioningRequest(input) {
1567
+ const configResult = ProviderProvisioningConfigV1Schema.safeParse(input.config);
1568
+ if (!configResult.success) {
1569
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning config is invalid");
1570
+ }
1571
+ const config = configResult.data;
1572
+ const headerResult = ProviderProvisioningHeaderV1Schema.safeParse({
1573
+ ...input.header,
1574
+ configDigest: await providerProvisioningConfigDigest(config)
1575
+ });
1576
+ if (!headerResult.success) {
1577
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning header is invalid");
1578
+ }
1579
+ const header = headerResult.data;
1580
+ const needsSecret = isSecretOperation(header.operation);
1581
+ if (needsSecret !== (input.secret !== void 0) || needsSecret !== (input.recipient !== void 0)) {
1582
+ throw new ByokCoreError(
1583
+ "provider_provisioning_request_invalid",
1584
+ "A secret and recipient are required exactly for configure and replace_secret"
1585
+ );
1586
+ }
1587
+ let sealed;
1588
+ if (needsSecret) {
1589
+ const recipient = input.recipient;
1590
+ const publicJwk = parseSealingPublicJwk(recipient.publicJwk);
1591
+ if (await deriveSealingKeyId(publicJwk) !== recipient.keyId) {
1592
+ throw new ByokCoreError("sealed_secret_invalid", "Sealing key id does not match its public key");
1593
+ }
1594
+ const { enc, ciphertext } = await hpkeSealBase({
1595
+ recipientPublicKey: sealingPublicKeyBytes(publicJwk),
1596
+ info: INFO_BYTES(),
1597
+ aad: providerSecretAadBytes(header, recipient.keyId),
1598
+ plaintext: padProviderSecret(input.secret)
1599
+ });
1600
+ sealed = {
1601
+ suite: PROVIDER_SECRET_HPKE_SUITE,
1602
+ keyId: recipient.keyId,
1603
+ enc: encodeBase64Url(enc),
1604
+ ciphertext: encodeBase64Url(ciphertext)
1605
+ };
1606
+ }
1607
+ return parseProviderProvisioningRequest({
1608
+ version: PROVIDER_PROVISIONING_REQUEST_VERSION,
1609
+ header,
1610
+ config,
1611
+ ...sealed === void 0 ? {} : { sealed }
1612
+ });
1613
+ }
1614
+ async function openProviderProvisioningSecret(input) {
1615
+ const request = parseProviderProvisioningRequest(input.request);
1616
+ const sealed = request.sealed;
1617
+ if (sealed === void 0) {
1618
+ throw new ByokCoreError("provider_provisioning_request_invalid", "Provider provisioning request carries no sealed secret");
1619
+ }
1620
+ const publicJwk = parseSealingPublicJwk(input.recipient.publicJwk);
1621
+ if (sealed.keyId !== input.recipient.keyId || await deriveSealingKeyId(publicJwk) !== input.recipient.keyId) {
1622
+ throw new ByokCoreError("sealed_secret_invalid", "Sealed secret is not addressed to this sealing key");
1623
+ }
1624
+ await assertProviderProvisioningConfigDigest(request);
1625
+ const ciphertext = decodeBase64Url(sealed.ciphertext, "ciphertext");
1626
+ const paddedLength = ciphertext.byteLength - HPKE_AEAD_TAG_BYTES;
1627
+ if (paddedLength < PROVIDER_SECRET_PAD_BLOCK_BYTES || paddedLength > PROVIDER_SECRET_MAX_PADDED_BYTES || paddedLength % PROVIDER_SECRET_PAD_BLOCK_BYTES !== 0) {
1628
+ throw new ByokCoreError("sealed_secret_invalid", "Sealed secret ciphertext has an invalid length");
1629
+ }
1630
+ const padded = await hpkeOpenBase({
1631
+ recipientPrivateKey: input.recipient.privateKey,
1632
+ recipientPublicKey: sealingPublicKeyBytes(publicJwk),
1633
+ enc: decodeBase64Url(sealed.enc, "enc"),
1634
+ info: INFO_BYTES(),
1635
+ aad: providerSecretAadBytes(request.header, sealed.keyId),
1636
+ ciphertext
1637
+ });
1638
+ try {
1639
+ return unpadProviderSecret(padded);
1640
+ } finally {
1641
+ padded.fill(0);
1642
+ }
1643
+ }
1644
+
1079
1645
  // src/pairing.ts
1080
1646
  var NONCE_SIGNING_DOMAIN = "byok-nonce-v1\n";
1081
1647
  function nonceSigningBytes(nonce) {
@@ -2234,6 +2800,6 @@ function createInMemoryCoreCompositionWithClock() {
2234
2800
  return { stores: createInMemoryCoreStores({ clock }).stores, clock };
2235
2801
  }
2236
2802
 
2237
- export { BOARD_STATUSES, BOARD_TRANSITIONS, ByokCoreError, CANONICAL_TIMESTAMP_PATTERN, CAPABILITY_DECLARATION_SCHEMA_ID, CAPABILITY_NAME_PATTERN, CONTENT_HASH_PATTERN, CORE_ERROR_CODES, CORE_NON_COMPOSITION_PORT_NAMES, CORE_PORT_INTERFACES, CORE_PORT_METHODS, CORE_STORE_NAMES, CapabilityDeclarationSchema, CoreConflictError, DEVICE_ASSERTION_ALGORITHMS, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_DOMAIN_PREFIX, DEVICE_ASSERTION_MAX_TTL_MS, DEVICE_ASSERTION_SCHEMA_ID, DEVICE_ASSERTION_VERSION, DEVICE_PROOF_ALGORITHMS, DEVICE_PROOF_DOMAIN_PREFIX, DEVICE_PROOF_HEADER, DEVICE_PROOF_SCHEMA_ID, DEVICE_PROOF_VERSION, DeviceAssertionClaimsSchema, DeviceAssertionEnvelopeV1Schema, DeviceProofEnvelopeV1Schema, DeviceProofProtectedClaimsSchema, IN_MEMORY_CLOCK_EPOCH, InMemoryBoardStore, InMemoryDeviceAssertionReplayAuthority, InMemoryMailboxStore, InMemoryObjectStore, InMemoryPresenceStore, InMemoryQuotaStore, InMemorySkillPackStore, InMemoryTruthStore, MAILBOX_MESSAGE_STATES, NONCE_SIGNING_DOMAIN, OBJECT_KEY_PREFIX_PATTERN, OBJECT_STATES, OBJECT_STATE_TRANSITIONS, PRESENCE_LEVELS, PRINCIPAL_KINDS, SKILL_FRONTMATTER_FIELDS, SKILL_PACK_DESCRIPTION_MAX_LENGTH, SKILL_PACK_ENTRY_PATH, SKILL_PACK_FILE_MAX_BYTES, SKILL_PACK_FILE_PATH_MAX_LENGTH, SKILL_PACK_FILE_PATH_PATTERN, SKILL_PACK_FORBIDDEN_FIELDS, SKILL_PACK_MANIFEST_SCHEMA_ID, SKILL_PACK_MAX_BYTES, SKILL_PACK_MAX_FILES, SKILL_PACK_NAME_MAX_LENGTH, SKILL_PACK_NAME_PATTERN, SKILL_PACK_REJECTIONS, SKILL_PACK_VERSION_PATTERN, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS, STORAGE_RESERVATION_STATES, STORAGE_WRITE_KINDS, STORAGE_WRITE_POSTURES, SkillPackFileSchema, SkillPackManifestSchema, TASK_ASSERTION_AGENT_REF_MAX_BYTES, TASK_ASSERTION_DOMAIN_PREFIX, TASK_ASSERTION_SCHEMA_ID, TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH, TASK_ASSERTION_VERSION, TENANT_ID_MAX_LENGTH, TENANT_KEY_SEPARATOR, TRUTH_RECORD_KINDS, TaskAssertionAgentRefSchema, TaskAssertionClaimsSchema, TaskAssertionEnvelopeV1Schema, assertCanonicalTimestamp, assertCapability, authenticateDeviceAssertion, authenticateTaskAssertion, canonicalizeJson, canonicalizeJsonBytes, checkSkillPackEntry, checkSkillPackFileContent, checkSkillPackManifest, contentHash, createInMemoryCoreCompositionWithClock, createInMemoryCoreStores, createMutableClock, deviceAssertionCanonicalClaims, deviceAssertionCanonicalJson, deviceAssertionSigningInput, deviceProofCanonicalClaims, deviceProofCanonicalJson, deviceProofSigningInput, hasCapability, isCanonicalTimestamp, isContentHash, isControlPlanePrincipal, isCoreConflictError, isCoreError, isDevicePrincipal, isLegalBoardTransition, isLegalObjectTransition, isSkillPackPathSafe, isTenantId, nonceSigningBytes, objectKeyPrefix, parseCapabilityDeclaration, parseDeviceAssertionEnvelope, parseDeviceProofEnvelope, parseSkillFrontmatter, parseSkillPackManifest, parseTaskAssertionEnvelope, principalTenant, skillPackContentHashInput, taskAssertionCanonicalClaims, taskAssertionCanonicalJson, taskAssertionSigningInput, tenantId, tenantKey, tenantObjectKey, verifyDeviceAssertion, verifyTaskAssertion };
2803
+ export { BOARD_STATUSES, BOARD_TRANSITIONS, ByokCoreError, CANONICAL_TIMESTAMP_PATTERN, CAPABILITY_DECLARATION_SCHEMA_ID, CAPABILITY_NAME_PATTERN, CONTENT_HASH_PATTERN, CORE_ERROR_CODES, CORE_NON_COMPOSITION_PORT_NAMES, CORE_PORT_INTERFACES, CORE_PORT_METHODS, CORE_STORE_NAMES, CapabilityDeclarationSchema, CoreConflictError, DEVICE_ASSERTION_ALGORITHMS, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_DOMAIN_PREFIX, DEVICE_ASSERTION_MAX_TTL_MS, DEVICE_ASSERTION_SCHEMA_ID, DEVICE_ASSERTION_VERSION, DEVICE_PROOF_ALGORITHMS, DEVICE_PROOF_DOMAIN_PREFIX, DEVICE_PROOF_HEADER, DEVICE_PROOF_SCHEMA_ID, DEVICE_PROOF_VERSION, DeviceAssertionClaimsSchema, DeviceAssertionEnvelopeV1Schema, DeviceProofEnvelopeV1Schema, DeviceProofProtectedClaimsSchema, IN_MEMORY_CLOCK_EPOCH, InMemoryBoardStore, InMemoryDeviceAssertionReplayAuthority, InMemoryMailboxStore, InMemoryObjectStore, InMemoryPresenceStore, InMemoryQuotaStore, InMemorySkillPackStore, InMemoryTruthStore, MAILBOX_MESSAGE_STATES, NONCE_SIGNING_DOMAIN, OBJECT_KEY_PREFIX_PATTERN, OBJECT_STATES, OBJECT_STATE_TRANSITIONS, PRESENCE_LEVELS, PRINCIPAL_KINDS, PROVIDER_PROVISIONING_MAX_TTL_MS, PROVIDER_PROVISIONING_OPERATIONS, PROVIDER_PROVISIONING_REQUEST_VERSION, PROVIDER_PROVISIONING_SECRET_OPERATIONS, PROVIDER_SECRET_HPKE_INFO, PROVIDER_SECRET_HPKE_SUITE, PROVIDER_SECRET_MAX_BYTES, PROVIDER_SECRET_MAX_PADDED_BYTES, PROVIDER_SECRET_PAD_BLOCK_BYTES, ProviderProvisioningConfigV1Schema, ProviderProvisioningExpectedProfileSchema, ProviderProvisioningHeaderV1Schema, ProviderProvisioningIdentityRevisionSchema, ProviderProvisioningRequestV1Schema, SEALING_KEY_ID_LENGTH, SKILL_FRONTMATTER_FIELDS, SKILL_PACK_DESCRIPTION_MAX_LENGTH, SKILL_PACK_ENTRY_PATH, SKILL_PACK_FILE_MAX_BYTES, SKILL_PACK_FILE_PATH_MAX_LENGTH, SKILL_PACK_FILE_PATH_PATTERN, SKILL_PACK_FORBIDDEN_FIELDS, SKILL_PACK_MANIFEST_SCHEMA_ID, SKILL_PACK_MAX_BYTES, SKILL_PACK_MAX_FILES, SKILL_PACK_NAME_MAX_LENGTH, SKILL_PACK_NAME_PATTERN, SKILL_PACK_REJECTIONS, SKILL_PACK_VERSION_PATTERN, STORAGE_ERROR_CODES, STORAGE_ERROR_HTTP_STATUS, STORAGE_RESERVATION_STATES, STORAGE_WRITE_KINDS, STORAGE_WRITE_POSTURES, SealedProviderSecretV1Schema, SealingKeyClaimV1Schema, SealingKeyIdSchema, SealingPublicJwkSchema, SkillPackFileSchema, SkillPackManifestSchema, TASK_ASSERTION_AGENT_REF_MAX_BYTES, TASK_ASSERTION_DOMAIN_PREFIX, TASK_ASSERTION_SCHEMA_ID, TASK_ASSERTION_TOOLSET_ID_MAX_LENGTH, TASK_ASSERTION_VERSION, TENANT_ID_MAX_LENGTH, TENANT_KEY_SEPARATOR, TRUTH_RECORD_KINDS, TaskAssertionAgentRefSchema, TaskAssertionClaimsSchema, TaskAssertionEnvelopeV1Schema, assertCanonicalTimestamp, assertCapability, assertProviderProvisioningConfigDigest, authenticateDeviceAssertion, authenticateTaskAssertion, canonicalizeJson, canonicalizeJsonBytes, checkSkillPackEntry, checkSkillPackFileContent, checkSkillPackManifest, contentHash, createInMemoryCoreCompositionWithClock, createInMemoryCoreStores, createMutableClock, deriveSealingKeyId, deviceAssertionCanonicalClaims, deviceAssertionCanonicalJson, deviceAssertionSigningInput, deviceProofCanonicalClaims, deviceProofCanonicalJson, deviceProofSigningInput, hasCapability, isCanonicalTimestamp, isContentHash, isControlPlanePrincipal, isCoreConflictError, isCoreError, isDevicePrincipal, isLegalBoardTransition, isLegalObjectTransition, isSkillPackPathSafe, isTenantId, nonceSigningBytes, objectKeyPrefix, openProviderProvisioningSecret, parseCapabilityDeclaration, parseDeviceAssertionEnvelope, parseDeviceProofEnvelope, parseProviderProvisioningRequest, parseSkillFrontmatter, parseSkillPackManifest, parseTaskAssertionEnvelope, principalTenant, providerProvisioningConfigDigest, providerProvisioningRequestDigest, providerSecretAadBytes, sealProviderProvisioningRequest, sealingKeyClaimCanonicalBytes, skillPackContentHashInput, taskAssertionCanonicalClaims, taskAssertionCanonicalJson, taskAssertionSigningInput, tenantId, tenantKey, tenantObjectKey, verifyDeviceAssertion, verifyTaskAssertion };
2238
2804
  //# sourceMappingURL=index.js.map
2239
2805
  //# sourceMappingURL=index.js.map