@kya-os/mcp-i 1.7.9 → 1.7.10

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.
@@ -254,11 +254,13 @@ class DelegationCredentialVerifier {
254
254
  durationMs: Date.now() - startTime,
255
255
  };
256
256
  }
257
- // Extract signature bytes - handle JWS format (header.payload.signature)
258
- // JWS compact serialization splits into 3 parts; only the 3rd is the signature
257
+ // Extract signature bytes and construct signing input
258
+ // Handle both JWS format (header.payload.signature) and proofValue format
259
259
  let signatureBase64url;
260
+ let signingInput;
260
261
  if (rawProofValue.includes('.')) {
261
- // JWS format: extract the signature (third segment)
262
+ // JWS format with b64:false (standard for Ed25519Signature2020)
263
+ // The signing input is: base64url(header) + "." + payload_bytes
262
264
  const jwsParts = rawProofValue.split('.');
263
265
  if (jwsParts.length !== 3) {
264
266
  return {
@@ -267,36 +269,49 @@ class DelegationCredentialVerifier {
267
269
  durationMs: Date.now() - startTime,
268
270
  };
269
271
  }
270
- signatureBase64url = jwsParts[2];
272
+ const [headerB64, payloadB64, signatureB64] = jwsParts;
273
+ signatureBase64url = signatureB64;
274
+ // For b64:false JWS, the signing input is the detached payload format:
275
+ // ASCII(BASE64URL(UTF8(JWS Protected Header))) || '.' || JWS Payload
276
+ // The payload is the hash of the document (for Ed25519Signature2020)
277
+ // Per RFC 7797 (JWS Unencoded Payload Option)
278
+ // Construct proof options for hashing (everything in proof except signature fields)
279
+ const proofOptions = { ...vc.proof };
280
+ delete proofOptions.proofValue;
281
+ delete proofOptions.jws;
282
+ delete proofOptions.signatureValue;
283
+ const canonicalDoc = (0, json_canonicalize_1.canonicalize)(vcWithoutProof);
284
+ const canonicalProof = (0, json_canonicalize_1.canonicalize)(proofOptions);
285
+ // Hash both and concatenate (payload for JWS)
286
+ const docHash = (0, crypto_1.createHash)('sha256').update(canonicalDoc, 'utf8').digest();
287
+ const proofHash = (0, crypto_1.createHash)('sha256').update(canonicalProof, 'utf8').digest();
288
+ const payloadBytes = Buffer.concat([proofHash, docHash]);
289
+ // JWS signing input: header + "." + payload_bytes
290
+ const headerBytes = Buffer.from(headerB64, 'utf8');
291
+ const separator = Buffer.from('.', 'utf8');
292
+ signingInput = Buffer.concat([headerBytes, separator, payloadBytes]);
271
293
  }
272
294
  else {
273
- // proofValue is raw base64url signature
295
+ // proofValue is raw base64url signature (Ed25519Signature2020 native format)
274
296
  signatureBase64url = rawProofValue;
275
- }
276
- try {
277
- // For Ed25519Signature2020, we need to:
278
- // 1. Canonicalize the VC without proofValue
279
- // 2. Canonicalize the proof options
280
- // 3. Hash both and concatenate
281
- // 4. Verify the signature
282
297
  // Construct proof options (everything in proof except proofValue)
283
298
  const proofOptions = { ...vc.proof };
284
299
  delete proofOptions.proofValue;
285
300
  delete proofOptions.jws;
286
301
  delete proofOptions.signatureValue;
287
- // Create signing input: hash(canonicalize(document)) + hash(canonicalize(proofOptions))
302
+ // Create signing input: hash(canonicalize(proofOptions)) || hash(canonicalize(document))
288
303
  const canonicalDoc = (0, json_canonicalize_1.canonicalize)(vcWithoutProof);
289
304
  const canonicalProof = (0, json_canonicalize_1.canonicalize)(proofOptions);
290
- // For Ed25519Signature2020, signing input is:
291
- // SHA-256(canonicalize(proofOptions)) || SHA-256(canonicalize(document))
292
305
  const docHash = (0, crypto_1.createHash)('sha256').update(canonicalDoc, 'utf8').digest();
293
306
  const proofHash = (0, crypto_1.createHash)('sha256').update(canonicalProof, 'utf8').digest();
294
- const signingInput = Buffer.concat([proofHash, docHash]);
307
+ signingInput = Buffer.concat([proofHash, docHash]);
308
+ }
309
+ try {
295
310
  // Decode the base64url signature
296
311
  const signatureBytes = Buffer.from(signatureBase64url, 'base64url');
297
312
  // Use subtle crypto for Ed25519 verification
298
313
  const cryptoKey = await globalThis.crypto.subtle.importKey('jwk', publicKeyJwk, { name: 'Ed25519' }, false, ['verify']);
299
- const isValid = await globalThis.crypto.subtle.verify({ name: 'Ed25519' }, cryptoKey, signatureBytes, signingInput);
314
+ const isValid = await globalThis.crypto.subtle.verify({ name: 'Ed25519' }, cryptoKey, new Uint8Array(signatureBytes), new Uint8Array(signingInput));
300
315
  if (!isValid) {
301
316
  return {
302
317
  valid: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-os/mcp-i",
3
- "version": "1.7.9",
3
+ "version": "1.7.10",
4
4
  "description": "The TypeScript MCP framework with identity features built-in",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",