@vess-id/ai-identity 0.0.1 → 0.0.2
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 +33 -12
- package/dist/index.d.mts +3004 -0
- package/dist/index.d.ts +3004 -0
- package/dist/index.js +266 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +261 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -366,7 +366,51 @@ async function getVerifier(publicKey) {
|
|
|
366
366
|
};
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
// src/did/did-utils.ts
|
|
370
|
+
function createDidJwk(publicKey) {
|
|
371
|
+
const publicJwk = {
|
|
372
|
+
kty: publicKey.kty,
|
|
373
|
+
crv: publicKey.crv,
|
|
374
|
+
x: publicKey.x,
|
|
375
|
+
y: publicKey.y,
|
|
376
|
+
use: publicKey.use,
|
|
377
|
+
alg: publicKey.alg
|
|
378
|
+
};
|
|
379
|
+
const cleanedJwk = Object.fromEntries(
|
|
380
|
+
Object.entries(publicJwk).filter(([_, v]) => v !== void 0)
|
|
381
|
+
);
|
|
382
|
+
const encoded = Buffer.from(JSON.stringify(cleanedJwk)).toString("base64url");
|
|
383
|
+
return `did:jwk:${encoded}`;
|
|
384
|
+
}
|
|
385
|
+
function extractPublicKey(privateKey) {
|
|
386
|
+
const { d, key_ops, ...publicKey } = privateKey;
|
|
387
|
+
return publicKey;
|
|
388
|
+
}
|
|
389
|
+
function extractPublicKeyFromDid(did) {
|
|
390
|
+
if (!did.startsWith("did:jwk:")) {
|
|
391
|
+
throw new Error("Only did:jwk format is supported");
|
|
392
|
+
}
|
|
393
|
+
const encoded = did.replace("did:jwk:", "");
|
|
394
|
+
return JSON.parse(Buffer.from(encoded, "base64url").toString());
|
|
395
|
+
}
|
|
396
|
+
function isValidDidJwk(did) {
|
|
397
|
+
if (!did.startsWith("did:jwk:")) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const encoded = did.replace("did:jwk:", "");
|
|
402
|
+
const decoded = JSON.parse(Buffer.from(encoded, "base64url").toString());
|
|
403
|
+
return typeof decoded === "object" && decoded.kty !== void 0;
|
|
404
|
+
} catch {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function getKeyIdFromDid(did) {
|
|
409
|
+
return `${did}#0`;
|
|
410
|
+
}
|
|
411
|
+
|
|
369
412
|
// src/utils/sdjwt-client.ts
|
|
413
|
+
import { subtle as subtle2 } from "crypto";
|
|
370
414
|
var SDJwtClient = class {
|
|
371
415
|
static instances = /* @__PURE__ */ new Map();
|
|
372
416
|
static keyManager;
|
|
@@ -571,6 +615,212 @@ var SDJwtClient = class {
|
|
|
571
615
|
verifierCount: this.verifierCache.size
|
|
572
616
|
};
|
|
573
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Create a verifier function from an external public key
|
|
620
|
+
* This is used for verifying SD-JWTs when you don't have the private key
|
|
621
|
+
* (e.g., API side verifying credentials issued by MCP)
|
|
622
|
+
*/
|
|
623
|
+
static async getVerifierFromPublicKey(publicKey) {
|
|
624
|
+
const key = await subtle2.importKey(
|
|
625
|
+
"jwk",
|
|
626
|
+
publicKey,
|
|
627
|
+
{
|
|
628
|
+
name: "ECDSA",
|
|
629
|
+
namedCurve: "P-256"
|
|
630
|
+
},
|
|
631
|
+
true,
|
|
632
|
+
["verify"]
|
|
633
|
+
);
|
|
634
|
+
return async (data, signatureBase64url) => {
|
|
635
|
+
const encoder = new TextEncoder();
|
|
636
|
+
const signature = Uint8Array.from(
|
|
637
|
+
atob(signatureBase64url.replace(/-/g, "+").replace(/_/g, "/")),
|
|
638
|
+
(c) => c.charCodeAt(0)
|
|
639
|
+
);
|
|
640
|
+
const isValid = await subtle2.verify(
|
|
641
|
+
{
|
|
642
|
+
name: "ECDSA",
|
|
643
|
+
hash: { name: "SHA-256" }
|
|
644
|
+
},
|
|
645
|
+
key,
|
|
646
|
+
signature,
|
|
647
|
+
encoder.encode(data)
|
|
648
|
+
);
|
|
649
|
+
return isValid;
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Get SDJwtVcInstance for verification with an external public key
|
|
654
|
+
* Used when verifying credentials without having the issuer's private key
|
|
655
|
+
*/
|
|
656
|
+
static async getVerificationInstance(publicKey) {
|
|
657
|
+
const cacheKey = `verify:${JSON.stringify(publicKey)}`;
|
|
658
|
+
if (!this.instances.has(cacheKey)) {
|
|
659
|
+
const dummySigner = async () => "dummy";
|
|
660
|
+
const verifier = await this.getVerifierFromPublicKey(publicKey);
|
|
661
|
+
const instance = new SDJwtVcInstance({
|
|
662
|
+
signer: dummySigner,
|
|
663
|
+
verifier,
|
|
664
|
+
signAlg: ES256.alg,
|
|
665
|
+
hasher: digest,
|
|
666
|
+
hashAlg: "sha-256",
|
|
667
|
+
saltGenerator: generateSalt
|
|
668
|
+
});
|
|
669
|
+
this.instances.set(cacheKey, instance);
|
|
670
|
+
}
|
|
671
|
+
return this.instances.get(cacheKey);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Get SDJwtVcInstance for decoding without verification
|
|
675
|
+
*/
|
|
676
|
+
static async getDecodingInstance() {
|
|
677
|
+
const cacheKey = "decode:dummy";
|
|
678
|
+
if (!this.instances.has(cacheKey)) {
|
|
679
|
+
const dummySigner = async () => "dummy";
|
|
680
|
+
const dummyVerifier = async () => false;
|
|
681
|
+
const instance = new SDJwtVcInstance({
|
|
682
|
+
signer: dummySigner,
|
|
683
|
+
verifier: dummyVerifier,
|
|
684
|
+
signAlg: ES256.alg,
|
|
685
|
+
hasher: digest,
|
|
686
|
+
hashAlg: "sha-256",
|
|
687
|
+
saltGenerator: generateSalt
|
|
688
|
+
});
|
|
689
|
+
this.instances.set(cacheKey, instance);
|
|
690
|
+
}
|
|
691
|
+
return this.instances.get(cacheKey);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Verify an SD-JWT with an external public key
|
|
695
|
+
* Use this when you have the issuer's public key but not their private key
|
|
696
|
+
*
|
|
697
|
+
* @param credential - The SD-JWT credential string
|
|
698
|
+
* @param publicKey - The issuer's public key (JWK format)
|
|
699
|
+
* @returns Verification result with valid flag and payload
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```typescript
|
|
703
|
+
* const publicKey = extractPublicKeyFromDid(issuerDid)
|
|
704
|
+
* const result = await SDJwtClient.verifyWithExternalKey(credential, publicKey)
|
|
705
|
+
* if (result.valid) {
|
|
706
|
+
* console.log('Verified claims:', result.payload.claims)
|
|
707
|
+
* }
|
|
708
|
+
* ```
|
|
709
|
+
*/
|
|
710
|
+
static async verifyWithExternalKey(credential, publicKey) {
|
|
711
|
+
try {
|
|
712
|
+
const sdJwtInstance = await this.getVerificationInstance(publicKey);
|
|
713
|
+
const verificationResult = await sdJwtInstance.verify(credential, {});
|
|
714
|
+
if (!verificationResult) {
|
|
715
|
+
return { valid: false, error: "Verification failed" };
|
|
716
|
+
}
|
|
717
|
+
const claims = await sdJwtInstance.getClaims(credential);
|
|
718
|
+
return {
|
|
719
|
+
valid: true,
|
|
720
|
+
payload: verificationResult.payload,
|
|
721
|
+
claims
|
|
722
|
+
};
|
|
723
|
+
} catch (error) {
|
|
724
|
+
return {
|
|
725
|
+
valid: false,
|
|
726
|
+
error: error.message
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Verify an SD-JWT by extracting the issuer's public key from the DID
|
|
732
|
+
* Automatically resolves did:jwk DIDs
|
|
733
|
+
*
|
|
734
|
+
* @param credential - The SD-JWT credential string
|
|
735
|
+
* @returns Verification result with valid flag and payload
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```typescript
|
|
739
|
+
* const result = await SDJwtClient.verifyWithIssuerDid(credential)
|
|
740
|
+
* if (result.valid) {
|
|
741
|
+
* console.log('Issuer:', result.payload.iss)
|
|
742
|
+
* }
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
static async verifyWithIssuerDid(credential) {
|
|
746
|
+
try {
|
|
747
|
+
const decoded = await this.decodeWithoutVerification(credential);
|
|
748
|
+
if (!decoded.payload) {
|
|
749
|
+
return { valid: false, error: "Failed to decode credential" };
|
|
750
|
+
}
|
|
751
|
+
const issuerDid = decoded.payload.iss || decoded.payload.issuer;
|
|
752
|
+
if (!issuerDid || typeof issuerDid !== "string") {
|
|
753
|
+
return { valid: false, error: "Issuer DID not found in credential" };
|
|
754
|
+
}
|
|
755
|
+
const publicKey = extractPublicKeyFromDid(issuerDid);
|
|
756
|
+
const result = await this.verifyWithExternalKey(credential, publicKey);
|
|
757
|
+
return {
|
|
758
|
+
...result,
|
|
759
|
+
issuerDid
|
|
760
|
+
};
|
|
761
|
+
} catch (error) {
|
|
762
|
+
return {
|
|
763
|
+
valid: false,
|
|
764
|
+
error: error.message
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Decode an SD-JWT without verification
|
|
770
|
+
* Use this when you need to inspect the credential before verification
|
|
771
|
+
* or when you don't have the issuer's public key
|
|
772
|
+
*
|
|
773
|
+
* WARNING: The returned payload has not been verified!
|
|
774
|
+
* Only use this for inspection purposes, not for authorization decisions.
|
|
775
|
+
*
|
|
776
|
+
* @param credential - The SD-JWT credential string
|
|
777
|
+
* @returns Decoded JWT payload, header, and disclosures
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* ```typescript
|
|
781
|
+
* const decoded = await SDJwtClient.decodeWithoutVerification(credential)
|
|
782
|
+
* console.log('Issuer (unverified):', decoded.payload?.iss)
|
|
783
|
+
* console.log('Disclosures:', decoded.disclosures?.length)
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
static async decodeWithoutVerification(credential) {
|
|
787
|
+
try {
|
|
788
|
+
const sdJwtInstance = await this.getDecodingInstance();
|
|
789
|
+
const decoded = await sdJwtInstance.decode(credential);
|
|
790
|
+
if (!decoded || !decoded.jwt) {
|
|
791
|
+
return { error: "Failed to decode SD-JWT" };
|
|
792
|
+
}
|
|
793
|
+
const claims = await decoded.getClaims(digest);
|
|
794
|
+
return {
|
|
795
|
+
payload: decoded.jwt.payload,
|
|
796
|
+
header: decoded.jwt.header,
|
|
797
|
+
disclosures: decoded.disclosures,
|
|
798
|
+
claims
|
|
799
|
+
};
|
|
800
|
+
} catch (error) {
|
|
801
|
+
return { error: error.message };
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Extract issuer DID from an SD-JWT without verification
|
|
806
|
+
* Useful for determining the issuer before verification
|
|
807
|
+
*
|
|
808
|
+
* @param credential - The SD-JWT credential string
|
|
809
|
+
* @returns The issuer DID or null if not found
|
|
810
|
+
*/
|
|
811
|
+
static extractIssuerDid(credential) {
|
|
812
|
+
try {
|
|
813
|
+
const parts = credential.split("~");
|
|
814
|
+
if (parts.length === 0) return null;
|
|
815
|
+
const jwt = parts[0];
|
|
816
|
+
const jwtParts = jwt.split(".");
|
|
817
|
+
if (jwtParts.length !== 3) return null;
|
|
818
|
+
const payload = JSON.parse(Buffer.from(jwtParts[1], "base64url").toString());
|
|
819
|
+
return payload.iss || payload.issuer || null;
|
|
820
|
+
} catch {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
574
824
|
};
|
|
575
825
|
|
|
576
826
|
// src/agent/agent-did-manager.ts
|
|
@@ -586,7 +836,7 @@ var AgentDIDManager = class {
|
|
|
586
836
|
*/
|
|
587
837
|
async generateAgentDID(agentId) {
|
|
588
838
|
const keyPair = await SDJwtClient.generateKeyPair();
|
|
589
|
-
const did =
|
|
839
|
+
const did = createDidJwk(keyPair.publicKey);
|
|
590
840
|
await this.keyManager.storeKey(did, keyPair.privateKey);
|
|
591
841
|
this.agentDIDMap.set(agentId, did);
|
|
592
842
|
await this.saveAgentDIDMapping(agentId, did);
|
|
@@ -628,7 +878,7 @@ var AgentDIDManager = class {
|
|
|
628
878
|
}
|
|
629
879
|
return {
|
|
630
880
|
privateKey,
|
|
631
|
-
publicKey:
|
|
881
|
+
publicKey: extractPublicKey(privateKey)
|
|
632
882
|
};
|
|
633
883
|
}
|
|
634
884
|
/**
|
|
@@ -670,31 +920,6 @@ var AgentDIDManager = class {
|
|
|
670
920
|
return [];
|
|
671
921
|
}
|
|
672
922
|
}
|
|
673
|
-
/**
|
|
674
|
-
* Create did:jwk from public key
|
|
675
|
-
*/
|
|
676
|
-
createDidJwk(publicKey) {
|
|
677
|
-
const publicJwk = {
|
|
678
|
-
kty: publicKey.kty,
|
|
679
|
-
crv: publicKey.crv,
|
|
680
|
-
x: publicKey.x,
|
|
681
|
-
y: publicKey.y,
|
|
682
|
-
use: publicKey.use,
|
|
683
|
-
alg: publicKey.alg
|
|
684
|
-
};
|
|
685
|
-
const encoded = Buffer.from(JSON.stringify(publicJwk)).toString("base64url");
|
|
686
|
-
return `did:jwk:${encoded}`;
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* Extract public key from private key
|
|
690
|
-
*/
|
|
691
|
-
extractPublicKey(privateKey) {
|
|
692
|
-
const { d, key_ops, ...publicKey } = privateKey;
|
|
693
|
-
return {
|
|
694
|
-
...publicKey
|
|
695
|
-
// Remove key_ops for public key
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
923
|
/**
|
|
699
924
|
* Save agent ID -> DID mapping to persistent storage
|
|
700
925
|
*/
|
|
@@ -911,7 +1136,7 @@ var UserIdentityManager = class {
|
|
|
911
1136
|
*/
|
|
912
1137
|
async createUserDID() {
|
|
913
1138
|
const keyPair = await SDJwtClient.generateKeyPair();
|
|
914
|
-
const did =
|
|
1139
|
+
const did = createDidJwk(keyPair.publicKey);
|
|
915
1140
|
await this.keyManager.storeKey(did, keyPair.privateKey);
|
|
916
1141
|
await this.saveUserDID(did);
|
|
917
1142
|
this.currentUserDID = did;
|
|
@@ -928,7 +1153,7 @@ var UserIdentityManager = class {
|
|
|
928
1153
|
}
|
|
929
1154
|
return {
|
|
930
1155
|
privateKey,
|
|
931
|
-
publicKey:
|
|
1156
|
+
publicKey: extractPublicKey(privateKey)
|
|
932
1157
|
};
|
|
933
1158
|
}
|
|
934
1159
|
/**
|
|
@@ -969,34 +1194,11 @@ var UserIdentityManager = class {
|
|
|
969
1194
|
await this.clearUserDID();
|
|
970
1195
|
return await this.createUserDID();
|
|
971
1196
|
}
|
|
972
|
-
/**
|
|
973
|
-
* Create did:jwk from public key
|
|
974
|
-
*/
|
|
975
|
-
createDidJwk(publicKey) {
|
|
976
|
-
const publicJwk = {
|
|
977
|
-
kty: publicKey.kty,
|
|
978
|
-
crv: publicKey.crv,
|
|
979
|
-
x: publicKey.x,
|
|
980
|
-
y: publicKey.y,
|
|
981
|
-
use: publicKey.use,
|
|
982
|
-
alg: publicKey.alg
|
|
983
|
-
};
|
|
984
|
-
const encoded = Buffer.from(JSON.stringify(publicJwk)).toString("base64url");
|
|
985
|
-
return `did:jwk:${encoded}`;
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Extract public key from private key
|
|
989
|
-
*/
|
|
990
|
-
extractPublicKey(privateKey) {
|
|
991
|
-
const { d, key_ops, ...publicKey } = privateKey;
|
|
992
|
-
return publicKey;
|
|
993
|
-
}
|
|
994
1197
|
/**
|
|
995
1198
|
* Resolve did:jwk locally
|
|
996
1199
|
*/
|
|
997
1200
|
resolveDidJwkLocally(did) {
|
|
998
|
-
const
|
|
999
|
-
const publicJwk = JSON.parse(Buffer.from(encoded, "base64url").toString());
|
|
1201
|
+
const publicJwk = extractPublicKeyFromDid(did);
|
|
1000
1202
|
return this.createDidDocument(did, publicJwk);
|
|
1001
1203
|
}
|
|
1002
1204
|
/**
|
|
@@ -2970,7 +3172,7 @@ var MetricsManager = class {
|
|
|
2970
3172
|
/**
|
|
2971
3173
|
* End tracking an operation
|
|
2972
3174
|
*/
|
|
2973
|
-
endOperation(
|
|
3175
|
+
endOperation(_operationId, success, error) {
|
|
2974
3176
|
const endTime = performance.now();
|
|
2975
3177
|
const operation = this.operations[this.operations.length - 1];
|
|
2976
3178
|
if (operation) {
|
|
@@ -4492,15 +4694,20 @@ export {
|
|
|
4492
4694
|
checkPermissionWithVP,
|
|
4493
4695
|
configure,
|
|
4494
4696
|
createAjv,
|
|
4697
|
+
createDidJwk,
|
|
4495
4698
|
defaultConstraintEvaluator,
|
|
4496
4699
|
evaluateConstraints,
|
|
4700
|
+
extractPublicKey,
|
|
4701
|
+
extractPublicKeyFromDid,
|
|
4497
4702
|
generateKeyPair,
|
|
4498
4703
|
generateNonce,
|
|
4499
4704
|
getClient,
|
|
4705
|
+
getKeyIdFromDid,
|
|
4500
4706
|
getRequiredRelations,
|
|
4501
4707
|
getRequiredScopes,
|
|
4502
4708
|
indexActions,
|
|
4503
4709
|
indexCapabilities,
|
|
4710
|
+
isValidDidJwk,
|
|
4504
4711
|
loadActionRegistryFromFile,
|
|
4505
4712
|
loadActionRegistryFromObject,
|
|
4506
4713
|
planDelegationForVC,
|