@mcp-i/core 0.1.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/LICENSE +21 -0
- package/README.md +390 -0
- package/dist/auth/handshake.d.ts +104 -0
- package/dist/auth/handshake.d.ts.map +1 -0
- package/dist/auth/handshake.js +230 -0
- package/dist/auth/handshake.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/types.d.ts +31 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +7 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/delegation/audience-validator.d.ts +9 -0
- package/dist/delegation/audience-validator.d.ts.map +1 -0
- package/dist/delegation/audience-validator.js +17 -0
- package/dist/delegation/audience-validator.js.map +1 -0
- package/dist/delegation/bitstring.d.ts +37 -0
- package/dist/delegation/bitstring.d.ts.map +1 -0
- package/dist/delegation/bitstring.js +117 -0
- package/dist/delegation/bitstring.js.map +1 -0
- package/dist/delegation/cascading-revocation.d.ts +45 -0
- package/dist/delegation/cascading-revocation.d.ts.map +1 -0
- package/dist/delegation/cascading-revocation.js +148 -0
- package/dist/delegation/cascading-revocation.js.map +1 -0
- package/dist/delegation/delegation-graph.d.ts +49 -0
- package/dist/delegation/delegation-graph.d.ts.map +1 -0
- package/dist/delegation/delegation-graph.js +99 -0
- package/dist/delegation/delegation-graph.js.map +1 -0
- package/dist/delegation/did-key-resolver.d.ts +64 -0
- package/dist/delegation/did-key-resolver.d.ts.map +1 -0
- package/dist/delegation/did-key-resolver.js +154 -0
- package/dist/delegation/did-key-resolver.js.map +1 -0
- package/dist/delegation/did-web-resolver.d.ts +83 -0
- package/dist/delegation/did-web-resolver.d.ts.map +1 -0
- package/dist/delegation/did-web-resolver.js +218 -0
- package/dist/delegation/did-web-resolver.js.map +1 -0
- package/dist/delegation/index.d.ts +21 -0
- package/dist/delegation/index.d.ts.map +1 -0
- package/dist/delegation/index.js +21 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/delegation/outbound-headers.d.ts +81 -0
- package/dist/delegation/outbound-headers.d.ts.map +1 -0
- package/dist/delegation/outbound-headers.js +139 -0
- package/dist/delegation/outbound-headers.js.map +1 -0
- package/dist/delegation/outbound-proof.d.ts +43 -0
- package/dist/delegation/outbound-proof.d.ts.map +1 -0
- package/dist/delegation/outbound-proof.js +52 -0
- package/dist/delegation/outbound-proof.js.map +1 -0
- package/dist/delegation/statuslist-manager.d.ts +44 -0
- package/dist/delegation/statuslist-manager.d.ts.map +1 -0
- package/dist/delegation/statuslist-manager.js +126 -0
- package/dist/delegation/statuslist-manager.js.map +1 -0
- package/dist/delegation/storage/memory-graph-storage.d.ts +70 -0
- package/dist/delegation/storage/memory-graph-storage.d.ts.map +1 -0
- package/dist/delegation/storage/memory-graph-storage.js +145 -0
- package/dist/delegation/storage/memory-graph-storage.js.map +1 -0
- package/dist/delegation/storage/memory-statuslist-storage.d.ts +19 -0
- package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +1 -0
- package/dist/delegation/storage/memory-statuslist-storage.js +33 -0
- package/dist/delegation/storage/memory-statuslist-storage.js.map +1 -0
- package/dist/delegation/utils.d.ts +49 -0
- package/dist/delegation/utils.d.ts.map +1 -0
- package/dist/delegation/utils.js +131 -0
- package/dist/delegation/utils.js.map +1 -0
- package/dist/delegation/vc-issuer.d.ts +56 -0
- package/dist/delegation/vc-issuer.d.ts.map +1 -0
- package/dist/delegation/vc-issuer.js +80 -0
- package/dist/delegation/vc-issuer.js.map +1 -0
- package/dist/delegation/vc-verifier.d.ts +112 -0
- package/dist/delegation/vc-verifier.d.ts.map +1 -0
- package/dist/delegation/vc-verifier.js +280 -0
- package/dist/delegation/vc-verifier.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +2 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.d.ts +23 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +82 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/middleware/index.d.ts +7 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +7 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/with-mcpi.d.ts +152 -0
- package/dist/middleware/with-mcpi.d.ts.map +1 -0
- package/dist/middleware/with-mcpi.js +472 -0
- package/dist/middleware/with-mcpi.js.map +1 -0
- package/dist/proof/errors.d.ts +49 -0
- package/dist/proof/errors.d.ts.map +1 -0
- package/dist/proof/errors.js +61 -0
- package/dist/proof/errors.js.map +1 -0
- package/dist/proof/generator.d.ts +65 -0
- package/dist/proof/generator.d.ts.map +1 -0
- package/dist/proof/generator.js +163 -0
- package/dist/proof/generator.js.map +1 -0
- package/dist/proof/index.d.ts +4 -0
- package/dist/proof/index.d.ts.map +1 -0
- package/dist/proof/index.js +4 -0
- package/dist/proof/index.js.map +1 -0
- package/dist/proof/verifier.d.ts +108 -0
- package/dist/proof/verifier.d.ts.map +1 -0
- package/dist/proof/verifier.js +299 -0
- package/dist/proof/verifier.js.map +1 -0
- package/dist/providers/base.d.ts +64 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +19 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/memory.d.ts +33 -0
- package/dist/providers/memory.d.ts.map +1 -0
- package/dist/providers/memory.js +102 -0
- package/dist/providers/memory.js.map +1 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +2 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +77 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +251 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/types/protocol.d.ts +320 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/protocol.js +229 -0
- package/dist/types/protocol.js.map +1 -0
- package/dist/utils/base58.d.ts +31 -0
- package/dist/utils/base58.d.ts.map +1 -0
- package/dist/utils/base58.js +104 -0
- package/dist/utils/base58.js.map +1 -0
- package/dist/utils/base64.d.ts +13 -0
- package/dist/utils/base64.d.ts.map +1 -0
- package/dist/utils/base64.js +99 -0
- package/dist/utils/base64.js.map +1 -0
- package/dist/utils/crypto-service.d.ts +37 -0
- package/dist/utils/crypto-service.d.ts.map +1 -0
- package/dist/utils/crypto-service.js +153 -0
- package/dist/utils/crypto-service.js.map +1 -0
- package/dist/utils/did-helpers.d.ts +156 -0
- package/dist/utils/did-helpers.d.ts.map +1 -0
- package/dist/utils/did-helpers.js +193 -0
- package/dist/utils/did-helpers.js.map +1 -0
- package/dist/utils/ed25519-constants.d.ts +18 -0
- package/dist/utils/ed25519-constants.d.ts.map +1 -0
- package/dist/utils/ed25519-constants.js +21 -0
- package/dist/utils/ed25519-constants.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +105 -0
- package/src/__tests__/integration/full-flow.test.ts +362 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +332 -0
- package/src/__tests__/utils/mock-providers.ts +319 -0
- package/src/__tests__/utils/node-crypto-provider.ts +93 -0
- package/src/auth/handshake.ts +411 -0
- package/src/auth/index.ts +11 -0
- package/src/auth/types.ts +40 -0
- package/src/delegation/__tests__/audience-validator.test.ts +110 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +624 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +623 -0
- package/src/delegation/__tests__/did-key-resolver.test.ts +265 -0
- package/src/delegation/__tests__/did-web-resolver.test.ts +467 -0
- package/src/delegation/__tests__/outbound-headers.test.ts +230 -0
- package/src/delegation/__tests__/outbound-proof.test.ts +179 -0
- package/src/delegation/__tests__/statuslist-manager.test.ts +515 -0
- package/src/delegation/__tests__/utils.test.ts +185 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +487 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +1029 -0
- package/src/delegation/audience-validator.ts +24 -0
- package/src/delegation/bitstring.ts +160 -0
- package/src/delegation/cascading-revocation.ts +224 -0
- package/src/delegation/delegation-graph.ts +143 -0
- package/src/delegation/did-key-resolver.ts +181 -0
- package/src/delegation/did-web-resolver.ts +270 -0
- package/src/delegation/index.ts +33 -0
- package/src/delegation/outbound-headers.ts +193 -0
- package/src/delegation/outbound-proof.ts +90 -0
- package/src/delegation/statuslist-manager.ts +219 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +42 -0
- package/src/delegation/utils.ts +189 -0
- package/src/delegation/vc-issuer.ts +137 -0
- package/src/delegation/vc-verifier.ts +440 -0
- package/src/index.ts +264 -0
- package/src/logging/__tests__/logger.test.ts +366 -0
- package/src/logging/index.ts +6 -0
- package/src/logging/logger.ts +91 -0
- package/src/middleware/__tests__/with-mcpi.test.ts +504 -0
- package/src/middleware/index.ts +16 -0
- package/src/middleware/with-mcpi.ts +766 -0
- package/src/proof/__tests__/proof-generator.test.ts +483 -0
- package/src/proof/__tests__/verifier.test.ts +488 -0
- package/src/proof/errors.ts +75 -0
- package/src/proof/generator.ts +255 -0
- package/src/proof/index.ts +22 -0
- package/src/proof/verifier.ts +449 -0
- package/src/providers/base.ts +68 -0
- package/src/providers/index.ts +15 -0
- package/src/providers/memory.ts +130 -0
- package/src/session/__tests__/session-manager.test.ts +342 -0
- package/src/session/index.ts +7 -0
- package/src/session/manager.ts +332 -0
- package/src/types/protocol.ts +596 -0
- package/src/utils/__tests__/base58.test.ts +281 -0
- package/src/utils/__tests__/base64.test.ts +239 -0
- package/src/utils/__tests__/crypto-service.test.ts +530 -0
- package/src/utils/__tests__/did-helpers.test.ts +156 -0
- package/src/utils/base58.ts +115 -0
- package/src/utils/base64.ts +116 -0
- package/src/utils/crypto-service.ts +209 -0
- package/src/utils/did-helpers.ts +210 -0
- package/src/utils/ed25519-constants.ts +23 -0
- package/src/utils/index.ts +9 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Generator Tests β @mcp-i/core
|
|
3
|
+
*
|
|
4
|
+
* Ported from packages/mcp-i-core/src/proof/__tests__/proof-generator.test.ts.
|
|
5
|
+
* All test logic is identical β only import paths change.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
9
|
+
import { canonicalize } from "json-canonicalize";
|
|
10
|
+
import {
|
|
11
|
+
ProofGenerator,
|
|
12
|
+
createProofResponse,
|
|
13
|
+
extractCanonicalData,
|
|
14
|
+
type ToolRequest,
|
|
15
|
+
type ToolResponse,
|
|
16
|
+
} from "../generator.js";
|
|
17
|
+
import type { ProofAgentIdentity } from "../generator.js";
|
|
18
|
+
import type { SessionContext } from "../../types/protocol.js";
|
|
19
|
+
|
|
20
|
+
// NodeCryptoProvider for test environment (Node.js)
|
|
21
|
+
import { NodeCryptoProvider } from "../../__tests__/utils/node-crypto-provider.js";
|
|
22
|
+
|
|
23
|
+
const cryptoProvider = new NodeCryptoProvider();
|
|
24
|
+
|
|
25
|
+
async function makeIdentity(): Promise<ProofAgentIdentity> {
|
|
26
|
+
const keyPair = await cryptoProvider.generateKeyPair();
|
|
27
|
+
return {
|
|
28
|
+
did: "did:web:example.com:agents:test-agent",
|
|
29
|
+
kid: "key-test-123",
|
|
30
|
+
privateKey: keyPair.privateKey,
|
|
31
|
+
publicKey: keyPair.publicKey,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeSession(): SessionContext {
|
|
36
|
+
return {
|
|
37
|
+
sessionId: "sess_test_123",
|
|
38
|
+
audience: "example.com",
|
|
39
|
+
nonce: "test-nonce-456",
|
|
40
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
41
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
42
|
+
lastActivity: Math.floor(Date.now() / 1000),
|
|
43
|
+
ttlMinutes: 30,
|
|
44
|
+
identityState: "anonymous",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("ProofGenerator", () => {
|
|
49
|
+
let proofGenerator: ProofGenerator;
|
|
50
|
+
let mockIdentity: ProofAgentIdentity;
|
|
51
|
+
let mockSession: SessionContext;
|
|
52
|
+
|
|
53
|
+
beforeEach(async () => {
|
|
54
|
+
mockIdentity = await makeIdentity();
|
|
55
|
+
mockSession = makeSession();
|
|
56
|
+
proofGenerator = new ProofGenerator(mockIdentity, cryptoProvider);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("Canonical Hash Generation", () => {
|
|
60
|
+
it("should generate consistent hashes for same request/response", async () => {
|
|
61
|
+
const request: ToolRequest = {
|
|
62
|
+
method: "test-tool",
|
|
63
|
+
params: { input: "hello" },
|
|
64
|
+
};
|
|
65
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
66
|
+
|
|
67
|
+
const proof1 = await proofGenerator.generateProof(
|
|
68
|
+
request,
|
|
69
|
+
response,
|
|
70
|
+
mockSession
|
|
71
|
+
);
|
|
72
|
+
const proof2 = await proofGenerator.generateProof(
|
|
73
|
+
request,
|
|
74
|
+
response,
|
|
75
|
+
mockSession
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(proof1.meta.requestHash).toBe(proof2.meta.requestHash);
|
|
79
|
+
expect(proof1.meta.responseHash).toBe(proof2.meta.responseHash);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should generate different hashes for different requests", async () => {
|
|
83
|
+
const request1: ToolRequest = {
|
|
84
|
+
method: "test-tool",
|
|
85
|
+
params: { input: "hello" },
|
|
86
|
+
};
|
|
87
|
+
const request2: ToolRequest = {
|
|
88
|
+
method: "test-tool",
|
|
89
|
+
params: { input: "goodbye" },
|
|
90
|
+
};
|
|
91
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
92
|
+
|
|
93
|
+
const proof1 = await proofGenerator.generateProof(
|
|
94
|
+
request1,
|
|
95
|
+
response,
|
|
96
|
+
mockSession
|
|
97
|
+
);
|
|
98
|
+
const proof2 = await proofGenerator.generateProof(
|
|
99
|
+
request2,
|
|
100
|
+
response,
|
|
101
|
+
mockSession
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(proof1.meta.requestHash).not.toBe(proof2.meta.requestHash);
|
|
105
|
+
expect(proof1.meta.responseHash).toBe(proof2.meta.responseHash);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should generate different hashes for different responses", async () => {
|
|
109
|
+
const request: ToolRequest = {
|
|
110
|
+
method: "test-tool",
|
|
111
|
+
params: { input: "hello" },
|
|
112
|
+
};
|
|
113
|
+
const response1: ToolResponse = { data: { output: "world" } };
|
|
114
|
+
const response2: ToolResponse = { data: { output: "universe" } };
|
|
115
|
+
|
|
116
|
+
const proof1 = await proofGenerator.generateProof(
|
|
117
|
+
request,
|
|
118
|
+
response1,
|
|
119
|
+
mockSession
|
|
120
|
+
);
|
|
121
|
+
const proof2 = await proofGenerator.generateProof(
|
|
122
|
+
request,
|
|
123
|
+
response2,
|
|
124
|
+
mockSession
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
expect(proof1.meta.requestHash).toBe(proof2.meta.requestHash);
|
|
128
|
+
expect(proof1.meta.responseHash).not.toBe(proof2.meta.responseHash);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should generate SHA-256 hashes with correct format", async () => {
|
|
132
|
+
const request: ToolRequest = {
|
|
133
|
+
method: "test-tool",
|
|
134
|
+
params: { input: "hello" },
|
|
135
|
+
};
|
|
136
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
137
|
+
|
|
138
|
+
const proof = await proofGenerator.generateProof(
|
|
139
|
+
request,
|
|
140
|
+
response,
|
|
141
|
+
mockSession
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(proof.meta.requestHash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
145
|
+
expect(proof.meta.responseHash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should handle requests without params", async () => {
|
|
149
|
+
const request: ToolRequest = { method: "simple-tool" };
|
|
150
|
+
const response: ToolResponse = { data: { result: "success" } };
|
|
151
|
+
|
|
152
|
+
const proof = await proofGenerator.generateProof(
|
|
153
|
+
request,
|
|
154
|
+
response,
|
|
155
|
+
mockSession
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(proof.meta.requestHash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
159
|
+
expect(proof.meta.responseHash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("Proof Metadata", () => {
|
|
164
|
+
it("should include all required metadata fields", async () => {
|
|
165
|
+
const request: ToolRequest = {
|
|
166
|
+
method: "test-tool",
|
|
167
|
+
params: { input: "hello" },
|
|
168
|
+
};
|
|
169
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
170
|
+
|
|
171
|
+
const proof = await proofGenerator.generateProof(
|
|
172
|
+
request,
|
|
173
|
+
response,
|
|
174
|
+
mockSession
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
expect(proof.meta.did).toBe(mockIdentity.did);
|
|
178
|
+
expect(proof.meta.kid).toBe(mockIdentity.kid);
|
|
179
|
+
expect(proof.meta.ts).toBeTypeOf("number");
|
|
180
|
+
expect(proof.meta.nonce).toBe(mockSession.nonce);
|
|
181
|
+
expect(proof.meta.audience).toBe(mockSession.audience);
|
|
182
|
+
expect(proof.meta.sessionId).toBe(mockSession.sessionId);
|
|
183
|
+
expect(proof.meta.requestHash).toBeTruthy();
|
|
184
|
+
expect(proof.meta.responseHash).toBeTruthy();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should include optional fields when provided", async () => {
|
|
188
|
+
const request: ToolRequest = {
|
|
189
|
+
method: "test-tool",
|
|
190
|
+
params: { input: "hello" },
|
|
191
|
+
};
|
|
192
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
193
|
+
|
|
194
|
+
const proof = await proofGenerator.generateProof(
|
|
195
|
+
request,
|
|
196
|
+
response,
|
|
197
|
+
mockSession,
|
|
198
|
+
{
|
|
199
|
+
scopeId: "orders.create",
|
|
200
|
+
delegationRef: "delegation-ref-123",
|
|
201
|
+
clientDid: "did:key:zClient",
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expect(proof.meta.scopeId).toBe("orders.create");
|
|
206
|
+
expect(proof.meta.delegationRef).toBe("delegation-ref-123");
|
|
207
|
+
expect(proof.meta.clientDid).toBe("did:key:zClient");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("JWS Generation", () => {
|
|
212
|
+
it("should generate compact JWS in correct format", async () => {
|
|
213
|
+
const request: ToolRequest = {
|
|
214
|
+
method: "test-tool",
|
|
215
|
+
params: { input: "hello" },
|
|
216
|
+
};
|
|
217
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
218
|
+
|
|
219
|
+
const proof = await proofGenerator.generateProof(
|
|
220
|
+
request,
|
|
221
|
+
response,
|
|
222
|
+
mockSession
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const jwsParts = proof.jws.split(".");
|
|
226
|
+
expect(jwsParts).toHaveLength(3);
|
|
227
|
+
expect(jwsParts[0]).toBeTruthy();
|
|
228
|
+
expect(jwsParts[1]).toBeTruthy();
|
|
229
|
+
expect(jwsParts[2]).toBeTruthy();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should use EdDSA algorithm", async () => {
|
|
233
|
+
const request: ToolRequest = {
|
|
234
|
+
method: "test-tool",
|
|
235
|
+
params: { input: "hello" },
|
|
236
|
+
};
|
|
237
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
238
|
+
|
|
239
|
+
const proof = await proofGenerator.generateProof(
|
|
240
|
+
request,
|
|
241
|
+
response,
|
|
242
|
+
mockSession
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const [headerB64] = proof.jws.split(".");
|
|
246
|
+
const header = JSON.parse(
|
|
247
|
+
Buffer.from(headerB64, "base64url").toString()
|
|
248
|
+
) as { alg: string; kid: string };
|
|
249
|
+
|
|
250
|
+
expect(header.alg).toBe("EdDSA");
|
|
251
|
+
expect(header.kid).toBe(mockIdentity.kid);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("should encode clientDid in the JWS payload when provided", async () => {
|
|
255
|
+
const request: ToolRequest = {
|
|
256
|
+
method: "test-tool",
|
|
257
|
+
params: { input: "hello" },
|
|
258
|
+
};
|
|
259
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
260
|
+
|
|
261
|
+
const proof = await proofGenerator.generateProof(
|
|
262
|
+
request,
|
|
263
|
+
response,
|
|
264
|
+
mockSession,
|
|
265
|
+
{ clientDid: "did:key:zClientPayload" }
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const payload = JSON.parse(
|
|
269
|
+
Buffer.from(proof.jws.split(".")[1], "base64url").toString()
|
|
270
|
+
) as { clientDid: string };
|
|
271
|
+
|
|
272
|
+
expect(payload.clientDid).toBe("did:key:zClientPayload");
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("Proof Verification", () => {
|
|
277
|
+
it("should verify valid proof structure", async () => {
|
|
278
|
+
const request: ToolRequest = {
|
|
279
|
+
method: "test-tool",
|
|
280
|
+
params: { input: "hello" },
|
|
281
|
+
};
|
|
282
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
283
|
+
|
|
284
|
+
const proof = await proofGenerator.generateProof(
|
|
285
|
+
request,
|
|
286
|
+
response,
|
|
287
|
+
mockSession
|
|
288
|
+
);
|
|
289
|
+
const isValid = await proofGenerator.verifyProof(
|
|
290
|
+
proof,
|
|
291
|
+
request,
|
|
292
|
+
response
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
expect(isValid).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should reject proof with mismatched request", async () => {
|
|
299
|
+
const request1: ToolRequest = {
|
|
300
|
+
method: "test-tool",
|
|
301
|
+
params: { input: "hello" },
|
|
302
|
+
};
|
|
303
|
+
const request2: ToolRequest = {
|
|
304
|
+
method: "test-tool",
|
|
305
|
+
params: { input: "goodbye" },
|
|
306
|
+
};
|
|
307
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
308
|
+
|
|
309
|
+
const proof = await proofGenerator.generateProof(
|
|
310
|
+
request1,
|
|
311
|
+
response,
|
|
312
|
+
mockSession
|
|
313
|
+
);
|
|
314
|
+
const isValid = await proofGenerator.verifyProof(
|
|
315
|
+
proof,
|
|
316
|
+
request2,
|
|
317
|
+
response
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
expect(isValid).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("should reject proof with mismatched response", async () => {
|
|
324
|
+
const request: ToolRequest = {
|
|
325
|
+
method: "test-tool",
|
|
326
|
+
params: { input: "hello" },
|
|
327
|
+
};
|
|
328
|
+
const response1: ToolResponse = { data: { output: "world" } };
|
|
329
|
+
const response2: ToolResponse = { data: { output: "universe" } };
|
|
330
|
+
|
|
331
|
+
const proof = await proofGenerator.generateProof(
|
|
332
|
+
request,
|
|
333
|
+
response1,
|
|
334
|
+
mockSession
|
|
335
|
+
);
|
|
336
|
+
const isValid = await proofGenerator.verifyProof(
|
|
337
|
+
proof,
|
|
338
|
+
request,
|
|
339
|
+
response2
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
expect(isValid).toBe(false);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("JSON Canonicalization", () => {
|
|
347
|
+
it("should canonicalize objects with sorted keys", () => {
|
|
348
|
+
const canonical = extractCanonicalData(
|
|
349
|
+
{ method: "test", params: { b: 2, a: 1 } },
|
|
350
|
+
{ data: { z: 26, a: 1 } }
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
expect(canonical.request).toEqual({
|
|
354
|
+
method: "test",
|
|
355
|
+
params: { b: 2, a: 1 },
|
|
356
|
+
});
|
|
357
|
+
expect(canonical.response).toEqual({ z: 26, a: 1 });
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe("Utility Functions", () => {
|
|
363
|
+
describe("createProofResponse", () => {
|
|
364
|
+
it("should create response with proof metadata", async () => {
|
|
365
|
+
const mockIdentity = await makeIdentity();
|
|
366
|
+
const mockSession = makeSession();
|
|
367
|
+
const request: ToolRequest = {
|
|
368
|
+
method: "test-tool",
|
|
369
|
+
params: { input: "hello" },
|
|
370
|
+
};
|
|
371
|
+
const data = { output: "world" };
|
|
372
|
+
|
|
373
|
+
const response = await createProofResponse(
|
|
374
|
+
request,
|
|
375
|
+
data,
|
|
376
|
+
mockIdentity,
|
|
377
|
+
mockSession,
|
|
378
|
+
cryptoProvider
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(response.data).toEqual(data);
|
|
382
|
+
expect(response.meta?.proof).toBeDefined();
|
|
383
|
+
expect(response.meta?.proof?.meta.did).toBe(mockIdentity.did);
|
|
384
|
+
expect(response.meta?.proof?.jws).toBeTruthy();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should include optional proof options", async () => {
|
|
388
|
+
const mockIdentity = await makeIdentity();
|
|
389
|
+
const mockSession = makeSession();
|
|
390
|
+
const request: ToolRequest = {
|
|
391
|
+
method: "test-tool",
|
|
392
|
+
params: { input: "hello" },
|
|
393
|
+
};
|
|
394
|
+
const data = { output: "world" };
|
|
395
|
+
|
|
396
|
+
const response = await createProofResponse(
|
|
397
|
+
request,
|
|
398
|
+
data,
|
|
399
|
+
mockIdentity,
|
|
400
|
+
mockSession,
|
|
401
|
+
cryptoProvider,
|
|
402
|
+
{ scopeId: "orders.create" }
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
expect(response.meta?.proof?.meta.scopeId).toBe("orders.create");
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe("RFC 8785 JCS Compliance", () => {
|
|
411
|
+
describe("Key Ordering", () => {
|
|
412
|
+
it("should sort object keys lexicographically", () => {
|
|
413
|
+
const obj = { z: 26, a: 1, m: 13 };
|
|
414
|
+
const result = canonicalize(obj);
|
|
415
|
+
expect(result).toBe('{"a":1,"m":13,"z":26}');
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe("Determinism", () => {
|
|
420
|
+
it("should produce identical output for same input", () => {
|
|
421
|
+
const obj = {
|
|
422
|
+
method: "test",
|
|
423
|
+
params: { nested: { z: 3, a: 1 }, array: [1, 2, 3] },
|
|
424
|
+
timestamp: 1234567890,
|
|
425
|
+
};
|
|
426
|
+
const result1 = canonicalize(obj);
|
|
427
|
+
const result2 = canonicalize(obj);
|
|
428
|
+
expect(result1).toBe(result2);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should produce identical output regardless of property order", () => {
|
|
432
|
+
const obj1 = { a: 1, b: 2, c: 3 };
|
|
433
|
+
const obj2 = { c: 3, a: 1, b: 2 };
|
|
434
|
+
const obj3 = { b: 2, c: 3, a: 1 };
|
|
435
|
+
|
|
436
|
+
expect(canonicalize(obj1)).toBe(canonicalize(obj2));
|
|
437
|
+
expect(canonicalize(obj2)).toBe(canonicalize(obj3));
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("Cross-Package Verification β key ordering and Unicode", () => {
|
|
443
|
+
let mockIdentity: ProofAgentIdentity;
|
|
444
|
+
let mockSession: SessionContext;
|
|
445
|
+
|
|
446
|
+
beforeEach(async () => {
|
|
447
|
+
mockIdentity = await makeIdentity();
|
|
448
|
+
mockSession = makeSession();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("should produce identical hashes for key-reordered requests", async () => {
|
|
452
|
+
const pg = new ProofGenerator(mockIdentity, cryptoProvider);
|
|
453
|
+
const response: ToolResponse = { data: { output: "world" } };
|
|
454
|
+
|
|
455
|
+
const proof1 = await pg.generateProof(
|
|
456
|
+
{ method: "test-tool", params: { z: 26, a: 1, m: 13 } },
|
|
457
|
+
response,
|
|
458
|
+
mockSession
|
|
459
|
+
);
|
|
460
|
+
const proof2 = await pg.generateProof(
|
|
461
|
+
{ method: "test-tool", params: { a: 1, m: 13, z: 26 } },
|
|
462
|
+
response,
|
|
463
|
+
mockSession
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
expect(proof1.meta.requestHash).toBe(proof2.meta.requestHash);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("should handle Unicode and special characters consistently", async () => {
|
|
470
|
+
const pg = new ProofGenerator(mockIdentity, cryptoProvider);
|
|
471
|
+
const request: ToolRequest = {
|
|
472
|
+
method: "test-tool",
|
|
473
|
+
params: { emoji: "π", text: "Hello δΈη", special: "\u0000\u001f" },
|
|
474
|
+
};
|
|
475
|
+
const response: ToolResponse = { data: { output: "π" } };
|
|
476
|
+
|
|
477
|
+
const proof1 = await pg.generateProof(request, response, mockSession);
|
|
478
|
+
const proof2 = await pg.generateProof(request, response, mockSession);
|
|
479
|
+
|
|
480
|
+
expect(proof1.meta.requestHash).toBe(proof2.meta.requestHash);
|
|
481
|
+
expect(proof1.meta.responseHash).toBe(proof2.meta.responseHash);
|
|
482
|
+
});
|
|
483
|
+
});
|