@peac/protocol 0.16.3 → 0.16.4

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { uuidv7 } from 'uuidv7';
2
- import { sign, decode, verify, jcsHash, sha256Hex, computeJwkThumbprint, jwkToPublicKeyBytes, base64urlDecode, signWire02, base64urlDecodeString, base64urlEncode } from '@peac/crypto';
2
+ import { sign, CryptoError, decode, verify, jcsHash, sha256Hex, computeJwkThumbprint, jwkToPublicKeyBytes, base64urlDecode, signWire02, base64urlDecodeString, base64urlEncode } from '@peac/crypto';
3
3
  export { base64urlDecode, base64urlEncode, computeJwkThumbprint, generateKeypair, jwkToPublicKeyBytes, sha256Bytes, sha256Hex, verify } from '@peac/crypto';
4
4
  import { isValidPurposeToken, isCanonicalPurpose, isValidPurposeReason, isValidWorkflowContext, createWorkflowContextInvalidError, hasValidDagSemantics, createWorkflowDagInvalidError, WORKFLOW_EXTENSION_KEY, validateKernelConstraints, createConstraintViolationError, ReceiptClaims, createEvidenceNotJsonError, validateSubjectSnapshot, isCanonicalIss, Wire02ClaimsSchema, PEAC_ISSUER_CONFIG_MAX_BYTES, validateRevokedKeys, PEAC_ISSUER_CONFIG_PATH, PEAC_POLICY_MAX_BYTES, PEAC_POLICY_PATH, PEAC_POLICY_FALLBACK_PATH, WARNING_TYP_MISSING, parseReceiptClaims, checkOccurredAtSkew, REGISTERED_RECEIPT_TYPES, WARNING_TYPE_UNREGISTERED, REGISTERED_EXTENSION_GROUP_KEYS, isValidExtensionKey, WARNING_UNKNOWN_EXTENSION, WARNING_EXTENSION_GROUP_MISSING, WARNING_EXTENSION_GROUP_MISMATCH, verifyPolicyBinding, sortWarnings, PEAC_RECEIPT_HEADER, PEAC_PURPOSE_HEADER, parsePurposeHeader, PEAC_PURPOSE_APPLIED_HEADER, PEAC_PURPOSE_REASON_HEADER, KERNEL_CONSTRAINTS } from '@peac/schema';
5
5
  import { VERIFIER_LIMITS, VERIFIER_NETWORK, TYPE_TO_EXTENSION_MAP, HASH, VERIFIER_POLICY_VERSION, VERIFICATION_REPORT_VERSION, WIRE_TYPE, OCCURRED_AT_TOLERANCE_SECONDS, ISS_CANONICAL, EXTENSION_BUDGET } from '@peac/kernel';
@@ -210,13 +210,32 @@ function validateTypeExtensionMappingInternal(input) {
210
210
  }
211
211
 
212
212
  // src/_internal/record-core/validators/jose-hardening.ts
213
- var KID_MAX_LENGTH = 256;
213
+ var KID_MAX_UTF8_BYTES = 256;
214
+ function isAcceptableKid(kid) {
215
+ if (typeof kid !== "string" || kid.length === 0) return false;
216
+ let bytes = 0;
217
+ for (let i = 0; i < kid.length; i++) {
218
+ const c = kid.charCodeAt(i);
219
+ if (c < 128) bytes += 1;
220
+ else if (c < 2048) bytes += 2;
221
+ else if (c >= 55296 && c <= 56319) {
222
+ const next = i + 1 < kid.length ? kid.charCodeAt(i + 1) : 0;
223
+ if (next < 56320 || next > 57343) return false;
224
+ bytes += 4;
225
+ i++;
226
+ } else if (c >= 56320 && c <= 57343) {
227
+ return false;
228
+ } else bytes += 3;
229
+ if (bytes > KID_MAX_UTF8_BYTES) return false;
230
+ }
231
+ return true;
232
+ }
214
233
  var ACCEPTED = { accepted: true };
215
234
  function rejected(errorCode) {
216
235
  return { accepted: false, errorCode };
217
236
  }
218
237
  function validateJoseHardeningInternal(header) {
219
- if (!header.kid || typeof header.kid !== "string" || header.kid.length === 0 || header.kid.length > KID_MAX_LENGTH) {
238
+ if (!isAcceptableKid(header.kid)) {
220
239
  return rejected("CRYPTO_JWS_MISSING_KID");
221
240
  }
222
241
  if (header.jwk !== void 0 || header.x5c !== void 0 || header.x5u !== void 0 || header.jku !== void 0) {
@@ -941,6 +960,25 @@ var IssueError = class extends Error {
941
960
  this.peacError = peacError;
942
961
  }
943
962
  };
963
+ var CRYPTO_ISSUANCE_CODE_MAP = {
964
+ CRYPTO_JWS_MISSING_KID: "E_JWS_MISSING_KID",
965
+ CRYPTO_IJSON_INVALID_STRING: "E_IJSON_INVALID_STRING",
966
+ CRYPTO_IJSON_NUMBER_OUT_OF_RANGE: "E_IJSON_NUMBER_OUT_OF_RANGE",
967
+ CRYPTO_IJSON_DUPLICATE_MEMBER_NAME: "E_IJSON_DUPLICATE_MEMBER_NAME"
968
+ };
969
+ function issueErrorFromCryptoError(err) {
970
+ const code = CRYPTO_ISSUANCE_CODE_MAP[err.code] ?? "E_INVALID_FORMAT";
971
+ const issueError = new IssueError({
972
+ code,
973
+ category: "validation",
974
+ severity: "error",
975
+ retryable: false,
976
+ http_status: 400,
977
+ details: { message: err.message }
978
+ });
979
+ issueError.cause = err;
980
+ return issueError;
981
+ }
944
982
  async function issueWire01(options) {
945
983
  if (!options.iss.startsWith("https://")) {
946
984
  throw new Error("Issuer URL must start with https://");
@@ -1149,7 +1187,15 @@ async function issueWire02(options) {
1149
1187
  });
1150
1188
  }
1151
1189
  }
1152
- const jws = await defaultCodec.encode(claims, options.privateKey, options.kid);
1190
+ let jws;
1191
+ try {
1192
+ jws = await defaultCodec.encode(claims, options.privateKey, options.kid);
1193
+ } catch (err) {
1194
+ if (err instanceof CryptoError) {
1195
+ throw issueErrorFromCryptoError(err);
1196
+ }
1197
+ throw err;
1198
+ }
1153
1199
  if (legacyPath && isShadowEnabled(options)) {
1154
1200
  scheduleShadow({
1155
1201
  call: "issue",