@agent-custody/receipts 0.1.2 → 0.1.3
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 +3 -0
- package/dist/crypto.d.ts +2 -0
- package/dist/crypto.js +5 -1
- package/dist/gateway.d.ts +3 -0
- package/dist/gateway.js +7 -2
- package/docs/usage.md +4 -0
- package/docs/verification.md +8 -0
- package/package.json +5 -3
- package/vectors/audit.json +147 -0
- package/vectors/canonical.json +76 -0
- package/vectors/merkle.json +200 -0
- package/vectors/receipts.json +682 -0
package/README.md
CHANGED
|
@@ -231,6 +231,7 @@ src/sdk/index.ts the interceptor: policy decision, record, wrap(tool fn)
|
|
|
231
231
|
src/sdk/claude.ts Claude Code command hook and Claude Agent SDK in-process hooks
|
|
232
232
|
src/sdk/openai-agents.ts, vercel-ai.ts, langchain.ts framework adapters, tested against the real packages
|
|
233
233
|
src/sidecar.ts the SDK issuer behind a local HTTP API, for agents in other languages
|
|
234
|
+
vectors/ conformance vectors: receipts, keys, logs, proofs, and expected verdicts; `bun run vectors` regenerates them
|
|
234
235
|
src/verify.ts offline verification, the human-readable report, and the audit that a later log extends an earlier one
|
|
235
236
|
src/cli.ts keygen, grant, gateway, hook, serve, log, verify, audit
|
|
236
237
|
src/index.ts the package's public surface; adapters are exported on ./sdk/<framework> subpaths
|
|
@@ -253,6 +254,8 @@ The design is two producers feeding one verifier. The SDK is the top of the funn
|
|
|
253
254
|
- SDK core: policy decision, record, and a generic `wrap(tool, fn)` for any framework whose tools are functions.
|
|
254
255
|
- Claude Code command hook for PreToolUse, PostToolUse, and PostToolUseFailure, with blocking on deny.
|
|
255
256
|
- Claude Agent SDK in-process hooks over the same handler.
|
|
257
|
+
- The forwarded call carries the receipt id and the attested agent and principal in `_meta`, so a stateful upstream can cite the receipt; the memory server in `@agent-custody/state` runs this way.
|
|
258
|
+
- Conformance vectors, generated by the test suite and published with the spec, and a browser verifier on agent-custody.dev that passes all of them.
|
|
256
259
|
- Sidecar: the SDK issuer behind a local HTTP API (`serve`), with a Python package on PyPI-ready footing and Go, Java, and Rust clients, so agents in any language get the same receipts from one signing implementation.
|
|
257
260
|
- Consistency proofs between tree heads (RFC 9162), served by the log and checked by the `audit` command, so an auditor holding an old tree head can prove nothing before it was rewritten.
|
|
258
261
|
- Remote log: the issuer can append to a log run by someone else over HTTP, whose key then signs the tree heads, so a verifier learns the receipt was in a log the operator could not rewrite. Includes the reference log server, bearer-token auth, and a root endpoint for auditors.
|
package/dist/crypto.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export declare function writeKeyPair(kp: KeyPair, dir: string, name: string): {
|
|
|
21
21
|
};
|
|
22
22
|
export declare function loadPrivateKey(path: string): KeyPair;
|
|
23
23
|
export declare function loadPublicKey(path: string): PublicKeyRef;
|
|
24
|
+
/** A public key from its SPKI PEM text, as found in a .pub file or a conformance vector. */
|
|
25
|
+
export declare function publicKeyFromPem(pem: string): PublicKeyRef;
|
|
24
26
|
export interface Envelope {
|
|
25
27
|
payloadType: string;
|
|
26
28
|
payload: string;
|
package/dist/crypto.js
CHANGED
|
@@ -50,7 +50,11 @@ export function loadPrivateKey(path) {
|
|
|
50
50
|
return { privateKey, publicKey, keyid: keyidOf(publicKey) };
|
|
51
51
|
}
|
|
52
52
|
export function loadPublicKey(path) {
|
|
53
|
-
|
|
53
|
+
return publicKeyFromPem(readFileSync(path, "utf8"));
|
|
54
|
+
}
|
|
55
|
+
/** A public key from its SPKI PEM text, as found in a .pub file or a conformance vector. */
|
|
56
|
+
export function publicKeyFromPem(pem) {
|
|
57
|
+
const publicKey = createPublicKey(pem);
|
|
54
58
|
return { publicKey, keyid: keyidOf(publicKey) };
|
|
55
59
|
}
|
|
56
60
|
function pae(payloadType, payload) {
|
package/dist/gateway.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { type Delegation } from "./delegation.ts";
|
|
|
4
4
|
export declare const GATEWAY_VERSION = "0.1.0";
|
|
5
5
|
export declare const RECEIPT_META_KEY = "agent-custody/receipt";
|
|
6
6
|
export declare const MODEL_META_KEY = "agent-custody/model";
|
|
7
|
+
/** Set by the gateway on the call it forwards upstream: the receipt id, and the agent and principal from the attested grant. */
|
|
8
|
+
export declare const AGENT_META_KEY = "agent-custody/agent";
|
|
9
|
+
export declare const PRINCIPAL_META_KEY = "agent-custody/principal";
|
|
7
10
|
export interface CallParams {
|
|
8
11
|
name: string;
|
|
9
12
|
arguments?: Record<string, unknown>;
|
package/dist/gateway.js
CHANGED
|
@@ -15,6 +15,9 @@ import { evaluate, policyDigest } from "./policy.js";
|
|
|
15
15
|
export const GATEWAY_VERSION = "0.1.0";
|
|
16
16
|
export const RECEIPT_META_KEY = "agent-custody/receipt";
|
|
17
17
|
export const MODEL_META_KEY = "agent-custody/model";
|
|
18
|
+
/** Set by the gateway on the call it forwards upstream: the receipt id, and the agent and principal from the attested grant. */
|
|
19
|
+
export const AGENT_META_KEY = "agent-custody/agent";
|
|
20
|
+
export const PRINCIPAL_META_KEY = "agent-custody/principal";
|
|
18
21
|
function resolveFactArgs(template, args) {
|
|
19
22
|
const out = {};
|
|
20
23
|
for (const [k, v] of Object.entries(template)) {
|
|
@@ -58,7 +61,7 @@ export async function createGateway(cfg) {
|
|
|
58
61
|
const issuer = createIssuer(gatewayKey, cfg.receiptsDir, openLog(cfg, gatewayKey));
|
|
59
62
|
const upstream = new Client({ name: "agent-custody-gateway", version: GATEWAY_VERSION });
|
|
60
63
|
await upstream.connect(new StdioClientTransport({ command: cfg.upstream.command, args: cfg.upstream.args, env: cfg.upstream.env, stderr: "inherit" }));
|
|
61
|
-
const callUpstream = async (name, args) => (await upstream.callTool({ name, arguments: args }));
|
|
64
|
+
const callUpstream = async (name, args, meta) => (await upstream.callTool({ name, arguments: args, ...(meta ? { _meta: meta } : {}) }));
|
|
62
65
|
async function gatherFacts(tool, args) {
|
|
63
66
|
const facts = {};
|
|
64
67
|
for (const f of cfg.facts.filter((f) => f.forTools.includes(tool))) {
|
|
@@ -98,7 +101,9 @@ export async function createGateway(cfg) {
|
|
|
98
101
|
}
|
|
99
102
|
if (policy.decision === "allow") {
|
|
100
103
|
try {
|
|
101
|
-
|
|
104
|
+
// The upstream learns which receipt this call is, and who the grant says is calling. An upstream that keeps
|
|
105
|
+
// state, such as the memory server, cites the receipt as the source of what it stores.
|
|
106
|
+
const result = await callUpstream(tool, args, { [RECEIPT_META_KEY]: receiptId, [AGENT_META_KEY]: delegation.agent, [PRINCIPAL_META_KEY]: delegation.principal });
|
|
102
107
|
execution = { status: result.isError ? "failed" : "executed", result, resultDigest: digestOf(result), provenance: "observed" };
|
|
103
108
|
}
|
|
104
109
|
catch (e) {
|
package/docs/usage.md
CHANGED
|
@@ -190,6 +190,10 @@ Any other MCP client works the same way: Python's `mcp` package, LangGraph's MCP
|
|
|
190
190
|
|
|
191
191
|
The receipt id is the file name under `receiptsDir`.
|
|
192
192
|
|
|
193
|
+
## What the upstream gets
|
|
194
|
+
|
|
195
|
+
The call the gateway forwards carries three `_meta` keys the agent cannot set: `agent-custody/receipt`, the id of the receipt being issued for this call; `agent-custody/agent` and `agent-custody/principal`, from the signed delegation grant. An upstream that keeps state can cite the receipt as the source of what it stores and record the attested caller rather than a claimed one. The memory server in `@agent-custody/state` does exactly that. Fact lookups do not carry them; only the forwarded call does.
|
|
196
|
+
|
|
193
197
|
## Operational notes
|
|
194
198
|
|
|
195
199
|
- **Money is integer minor units.** Cedar has no floating point. A float in `args` that a policy touches is an evaluation error, which is a deny.
|
package/docs/verification.md
CHANGED
|
@@ -98,6 +98,10 @@ A gateway receipt does **not** support:
|
|
|
98
98
|
- that the model named in `model` produced the call. No hosted provider signs model identity.
|
|
99
99
|
- that the gateway operator is honest. The operator holds the gateway key. Against a dishonest operator you need a log copy taken out of their control, or a signer they do not control. See the threat model table in the README.
|
|
100
100
|
|
|
101
|
+
## In the browser
|
|
102
|
+
|
|
103
|
+
[agent-custody.dev/verify](https://agent-custody.dev/verify) runs the same checks in the page over WebCrypto, nothing uploaded. It is a second implementation of this verifier, and it passes every published [conformance vector](https://agent-custody.dev/receipt/vectors).
|
|
104
|
+
|
|
101
105
|
## Programmatic verification
|
|
102
106
|
|
|
103
107
|
```ts
|
|
@@ -157,3 +161,7 @@ Programmatically, `auditExtends(older.treeHead, newer.treeHead, proof, keys)` re
|
|
|
157
161
|
|
|
158
162
|
The remote log also serves `GET /head`, its current tree head signed with the log's key, so an auditor can record heads on a schedule and later audit any two of them without holding a receipt for each.
|
|
159
163
|
|
|
164
|
+
## Conformance vectors
|
|
165
|
+
|
|
166
|
+
`vectors/` in the package holds fixed receipts, keys, logs, proofs, and the verdicts this verifier produces for them, generated by `bun run vectors` and checked by the test suite on every run. A verifier written elsewhere proves it agrees by reproducing every verdict. They are published at [agent-custody.dev/receipt/vectors](https://agent-custody.dev/receipt/vectors).
|
|
167
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-custody/receipts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Chain of custody for AI agents: signed, independently verifiable receipts for tool calls. MCP gateway + Cedar policy + Merkle transparency log",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -43,13 +43,15 @@
|
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"dist",
|
|
46
|
-
"docs"
|
|
46
|
+
"docs",
|
|
47
|
+
"vectors"
|
|
47
48
|
],
|
|
48
49
|
"scripts": {
|
|
49
50
|
"build": "tsc -p tsconfig.build.json",
|
|
50
51
|
"typecheck": "tsc --noEmit",
|
|
51
52
|
"test": "vitest run",
|
|
52
|
-
"demo": "tsx scripts/demo.ts"
|
|
53
|
+
"demo": "tsx scripts/demo.ts",
|
|
54
|
+
"vectors": "tsx scripts/vectors.ts"
|
|
53
55
|
},
|
|
54
56
|
"engines": {
|
|
55
57
|
"node": ">=22"
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2",
|
|
3
|
+
"note": "auditExtends(older, newer, proof, keys): both tree heads must verify against a listed key; the proof is the log's consistency proof between their sizes.",
|
|
4
|
+
"cases": [
|
|
5
|
+
{
|
|
6
|
+
"name": "remote-log-extends",
|
|
7
|
+
"description": "The tree head from the first remote receipt and the log's later head, with the proof the log served.",
|
|
8
|
+
"older": {
|
|
9
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
10
|
+
"payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
11
|
+
"signatures": [
|
|
12
|
+
{
|
|
13
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
14
|
+
"sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"newer": {
|
|
19
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
20
|
+
"payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
21
|
+
"signatures": [
|
|
22
|
+
{
|
|
23
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
24
|
+
"sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"proof": [
|
|
29
|
+
"4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
|
|
30
|
+
],
|
|
31
|
+
"keys": [
|
|
32
|
+
"log"
|
|
33
|
+
],
|
|
34
|
+
"expected": {
|
|
35
|
+
"ok": true,
|
|
36
|
+
"failing": []
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "remote-log-wrong-order",
|
|
41
|
+
"description": "The same heads the wrong way round.",
|
|
42
|
+
"older": {
|
|
43
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
44
|
+
"payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
45
|
+
"signatures": [
|
|
46
|
+
{
|
|
47
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
48
|
+
"sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"newer": {
|
|
53
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
54
|
+
"payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
55
|
+
"signatures": [
|
|
56
|
+
{
|
|
57
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
58
|
+
"sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"proof": [
|
|
63
|
+
"4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
|
|
64
|
+
],
|
|
65
|
+
"keys": [
|
|
66
|
+
"log"
|
|
67
|
+
],
|
|
68
|
+
"expected": {
|
|
69
|
+
"ok": false,
|
|
70
|
+
"failing": [
|
|
71
|
+
"older is not larger than newer"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "remote-log-wrong-key",
|
|
77
|
+
"description": "Tree heads checked against the app key, which did not sign them.",
|
|
78
|
+
"older": {
|
|
79
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
80
|
+
"payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
81
|
+
"signatures": [
|
|
82
|
+
{
|
|
83
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
84
|
+
"sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"newer": {
|
|
89
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
90
|
+
"payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
91
|
+
"signatures": [
|
|
92
|
+
{
|
|
93
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
94
|
+
"sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"proof": [
|
|
99
|
+
"4869289b5853ed3431c235a1f4e97d1d4205723e622dc71bd836018c62572723"
|
|
100
|
+
],
|
|
101
|
+
"keys": [
|
|
102
|
+
"app"
|
|
103
|
+
],
|
|
104
|
+
"expected": {
|
|
105
|
+
"ok": false,
|
|
106
|
+
"failing": [
|
|
107
|
+
"older tree head signature",
|
|
108
|
+
"newer tree head signature"
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "remote-log-bad-proof",
|
|
114
|
+
"description": "A proof with a hash removed.",
|
|
115
|
+
"older": {
|
|
116
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
117
|
+
"payload": "eyJyb290SGFzaCI6Ijg3MzM5M2E4ZTQ4ZmFkNzVlMmRkMzY1MzRjNmRjNzUzMTc5YmM0NmEzMTAxNDZjZTI2OGUxNTc2ODZiOGMwZTQiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExMFoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
118
|
+
"signatures": [
|
|
119
|
+
{
|
|
120
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
121
|
+
"sig": "vydFxb4Z753KUPOXLEFiYnE0nntaoU9xXtBe7IMW8eucYeef+XpGu+4+bXpo/zYZATcx4h+7bMmDd3frqizkBg=="
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"newer": {
|
|
126
|
+
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
127
|
+
"payload": "eyJyb290SGFzaCI6ImNlYmViNDAzYjNlZWYzYzgwNWVjYzg5OTI2NTM0ZGM0NjkxY2I4OWJlN2I0ZjFjNjE3Njg5MDFkMzIxNDkzYmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA2OjUyOjUxLjExNFoiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
128
|
+
"signatures": [
|
|
129
|
+
{
|
|
130
|
+
"keyid": "e1ed29255a4bd710006fe9ad333514e71c705608b769029088a818f57bcf080a",
|
|
131
|
+
"sig": "iyUBqiJcZd/RaEDXWhhxl34ekqy6tQ9qgsMQjAXlvuHS75aqPLeh0NRtUQB89LS2UMJYihmcSUdg/Z82uORqCQ=="
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
"proof": [],
|
|
136
|
+
"keys": [
|
|
137
|
+
"log"
|
|
138
|
+
],
|
|
139
|
+
"expected": {
|
|
140
|
+
"ok": false,
|
|
141
|
+
"failing": [
|
|
142
|
+
"newer log extends older log"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2",
|
|
3
|
+
"note": "canonical JSON: keys sorted, no whitespace, undefined dropped. digest = hex sha256 of the canonical string. keyid = hex sha256 of the SPKI DER public key. DSSE PAE = 'DSSEv1 ' + len(type) + ' ' + type + ' ' + len(payload) + ' ' + payload, signed with Ed25519.",
|
|
4
|
+
"values": [
|
|
5
|
+
{
|
|
6
|
+
"value": {
|
|
7
|
+
"b": 1,
|
|
8
|
+
"a": [
|
|
9
|
+
3,
|
|
10
|
+
{
|
|
11
|
+
"z": null,
|
|
12
|
+
"y": "ÿ"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"canonical": "{\"a\":[3,{\"y\":\"ÿ\",\"z\":null}],\"b\":1}",
|
|
17
|
+
"digest": "894fec1144463457d74d1eb8599cf2e04a42723765ec1d1aabb77d658d860f2b"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"value": "plain",
|
|
21
|
+
"canonical": "\"plain\"",
|
|
22
|
+
"digest": "945603a8f587786b463c3f94fce115c0fae88fac2728cc96ddf5981cf7f61741"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"value": 42,
|
|
26
|
+
"canonical": "42",
|
|
27
|
+
"digest": "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"value": [
|
|
31
|
+
1,
|
|
32
|
+
2,
|
|
33
|
+
3
|
|
34
|
+
],
|
|
35
|
+
"canonical": "[1,2,3]",
|
|
36
|
+
"digest": "a615eeaee21de5179de080de8c3052c8da901138406ba71c38c032845f7d54f4"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"value": {
|
|
40
|
+
"nested": {
|
|
41
|
+
"deep": {
|
|
42
|
+
"deeper": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"canonical": "{\"nested\":{\"deep\":{\"deeper\":true}}}",
|
|
47
|
+
"digest": "d64d85fa17b84e33888c36e22c9543527721099faee1e070943096aa3c9ba4c2"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"value": {},
|
|
51
|
+
"canonical": "{}",
|
|
52
|
+
"digest": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"keyid": {
|
|
56
|
+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEb81f6KaNmH1pNR7KbeIUo9DW65Y0Qsfdxqbtnhs7cg=\n-----END PUBLIC KEY-----\n",
|
|
57
|
+
"keyid": "0ecde3bf114156b895ceb4ea63daa345271e8ee979f47bc69cbf0266bb93f6f5"
|
|
58
|
+
},
|
|
59
|
+
"dsse": {
|
|
60
|
+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEb81f6KaNmH1pNR7KbeIUo9DW65Y0Qsfdxqbtnhs7cg=\n-----END PUBLIC KEY-----\n",
|
|
61
|
+
"envelope": {
|
|
62
|
+
"payloadType": "application/vnd.example+json",
|
|
63
|
+
"payload": "eyJoZWxsbyI6IndvcmxkIn0=",
|
|
64
|
+
"signatures": [
|
|
65
|
+
{
|
|
66
|
+
"keyid": "0ecde3bf114156b895ceb4ea63daa345271e8ee979f47bc69cbf0266bb93f6f5",
|
|
67
|
+
"sig": "wkH8ufuyC3BJldxmhFUCImAOo38KyQO/uQMITvTjgikPeE9GIv6OwYEFtQnjcBNzY0+MRTn++dGeMwvVKzIBDw=="
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"payloadJson": "{\"hello\":\"world\"}",
|
|
72
|
+
"paeHex": "445353457631203238206170706c69636174696f6e2f766e642e6578616d706c652b6a736f6e203137207b2268656c6c6f223a22776f726c64227d",
|
|
73
|
+
"paeSha256": "8a321059588971b8abbbfeb3a5c7f629a14fc46d2ecf12895a79483dde78532e",
|
|
74
|
+
"expected": true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2",
|
|
3
|
+
"note": "leafHash = sha256(0x00 || leaf); nodeHash = sha256(0x01 || left || right). Proof hashes are hex, leaf-to-root for inclusion; RFC 9162 order for consistency.",
|
|
4
|
+
"leaves": [
|
|
5
|
+
"leaf-0",
|
|
6
|
+
"leaf-1",
|
|
7
|
+
"leaf-2",
|
|
8
|
+
"leaf-3",
|
|
9
|
+
"leaf-4",
|
|
10
|
+
"leaf-5",
|
|
11
|
+
"leaf-6"
|
|
12
|
+
],
|
|
13
|
+
"leafHashes": [
|
|
14
|
+
"305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
|
|
15
|
+
"3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
|
|
16
|
+
"fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
|
|
17
|
+
"f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
|
|
18
|
+
"ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
|
|
19
|
+
"8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
|
|
20
|
+
"676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d"
|
|
21
|
+
],
|
|
22
|
+
"roots": [
|
|
23
|
+
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
24
|
+
"305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
|
|
25
|
+
"60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
|
|
26
|
+
"cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
|
|
27
|
+
"bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3",
|
|
28
|
+
"00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
|
|
29
|
+
"160cf1a616e8792f9078a9665cb06520d95a33f467d0826f2310219d31383d73",
|
|
30
|
+
"0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0"
|
|
31
|
+
],
|
|
32
|
+
"inclusion": [
|
|
33
|
+
{
|
|
34
|
+
"leafIndex": 0,
|
|
35
|
+
"treeSize": 7,
|
|
36
|
+
"hashes": [
|
|
37
|
+
"3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
|
|
38
|
+
"bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
|
|
39
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
40
|
+
],
|
|
41
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
42
|
+
"expected": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"leafIndex": 1,
|
|
46
|
+
"treeSize": 7,
|
|
47
|
+
"hashes": [
|
|
48
|
+
"305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
|
|
49
|
+
"bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
|
|
50
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
51
|
+
],
|
|
52
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
53
|
+
"expected": true
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"leafIndex": 2,
|
|
57
|
+
"treeSize": 7,
|
|
58
|
+
"hashes": [
|
|
59
|
+
"f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
|
|
60
|
+
"60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
|
|
61
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
62
|
+
],
|
|
63
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
64
|
+
"expected": true
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"leafIndex": 3,
|
|
68
|
+
"treeSize": 7,
|
|
69
|
+
"hashes": [
|
|
70
|
+
"fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
|
|
71
|
+
"60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
|
|
72
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
73
|
+
],
|
|
74
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
75
|
+
"expected": true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"leafIndex": 4,
|
|
79
|
+
"treeSize": 7,
|
|
80
|
+
"hashes": [
|
|
81
|
+
"8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
|
|
82
|
+
"676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d",
|
|
83
|
+
"bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
|
|
84
|
+
],
|
|
85
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
86
|
+
"expected": true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"leafIndex": 5,
|
|
90
|
+
"treeSize": 7,
|
|
91
|
+
"hashes": [
|
|
92
|
+
"ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
|
|
93
|
+
"676f3782f5b3a5fb4370ed49572cedc523f4a66322269c85f2af0509d17b0a4d",
|
|
94
|
+
"bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
|
|
95
|
+
],
|
|
96
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
97
|
+
"expected": true
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"leafIndex": 6,
|
|
101
|
+
"treeSize": 7,
|
|
102
|
+
"hashes": [
|
|
103
|
+
"985bb5d36b927800876871da925a7e82abe83a9ddba5882920a007a55ea2b376",
|
|
104
|
+
"bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
|
|
105
|
+
],
|
|
106
|
+
"root": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
107
|
+
"expected": true
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"consistency": [
|
|
111
|
+
{
|
|
112
|
+
"oldSize": 0,
|
|
113
|
+
"newSize": 7,
|
|
114
|
+
"oldRoot": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
115
|
+
"newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
116
|
+
"hashes": [],
|
|
117
|
+
"expected": true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"oldSize": 1,
|
|
121
|
+
"newSize": 7,
|
|
122
|
+
"oldRoot": "305df59f9590c3c9ac63d2b2743c388e3792449078cebf7fb3dbe6471643b2b7",
|
|
123
|
+
"newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
124
|
+
"hashes": [
|
|
125
|
+
"3145c409f259b7c53e32036090ff76751025a2498ba9823ef718cac50b4e616f",
|
|
126
|
+
"bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
|
|
127
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
128
|
+
],
|
|
129
|
+
"expected": true
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"oldSize": 2,
|
|
133
|
+
"newSize": 5,
|
|
134
|
+
"oldRoot": "60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
|
|
135
|
+
"newRoot": "00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
|
|
136
|
+
"hashes": [
|
|
137
|
+
"bd45ff28796704d88bdac51b1df553fda59837b616d6d1cb2114dbc3b087ff69",
|
|
138
|
+
"ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba"
|
|
139
|
+
],
|
|
140
|
+
"expected": true
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"oldSize": 3,
|
|
144
|
+
"newSize": 7,
|
|
145
|
+
"oldRoot": "cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
|
|
146
|
+
"newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
147
|
+
"hashes": [
|
|
148
|
+
"fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
|
|
149
|
+
"f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
|
|
150
|
+
"60a53eed0de87a90c8e59427c59c46253c33a76a09502a51801300927b7e6bdc",
|
|
151
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
152
|
+
],
|
|
153
|
+
"expected": true
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"oldSize": 4,
|
|
157
|
+
"newSize": 7,
|
|
158
|
+
"oldRoot": "bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3",
|
|
159
|
+
"newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
160
|
+
"hashes": [
|
|
161
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
162
|
+
],
|
|
163
|
+
"expected": true
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"oldSize": 5,
|
|
167
|
+
"newSize": 6,
|
|
168
|
+
"oldRoot": "00d21829a5503145348abcf712513eacf2a274211ad83e970202bb5b6d80b286",
|
|
169
|
+
"newRoot": "160cf1a616e8792f9078a9665cb06520d95a33f467d0826f2310219d31383d73",
|
|
170
|
+
"hashes": [
|
|
171
|
+
"ea9fc1a1b6e191b460d0d6306e3e870c173f39330f13cda1b70cfc72bdc398ba",
|
|
172
|
+
"8f1593cb92f429d9340b9bbc1f0bb122adf8026c42a4a42142e2168931727236",
|
|
173
|
+
"bdd1c5ff55b19cb6b0e7c761bf9a6ccaa27fbbfc07b74f1fabb6e911a0bd2ab3"
|
|
174
|
+
],
|
|
175
|
+
"expected": true
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"oldSize": 7,
|
|
179
|
+
"newSize": 7,
|
|
180
|
+
"oldRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
181
|
+
"newRoot": "0b007fb915eb9b2a146f54b1c86ec53b664f8e455b7660b0b6ee13edc0d921c0",
|
|
182
|
+
"hashes": [],
|
|
183
|
+
"expected": true
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"oldSize": 3,
|
|
187
|
+
"newSize": 7,
|
|
188
|
+
"oldRoot": "cf763a041c81ceef1578a6083f75c61bef2e0014f2a3e683a97fcfca5be7f19a",
|
|
189
|
+
"newRoot": "c5d3c9af8774ee745d99c20ddb54063f1e9abddf0900db1cfb9b3608eb50df4a",
|
|
190
|
+
"hashes": [
|
|
191
|
+
"fca89f57c9f8c8eb4047a7ff9d333acf9e0f3384b20b255bceab0f216dcca267",
|
|
192
|
+
"f76836325aec5699d8d71f8e42e9d47c5c29b08059ba296384f7ca40ad3a40ae",
|
|
193
|
+
"2edcc4da09ec6712e53fa29d2e571f7996455891323073556dd0c5ab8d0264c6",
|
|
194
|
+
"8eae6bd3b3a07f1f75ee72a531629e6eb31e42e62f760e47de52a53c3641ef23"
|
|
195
|
+
],
|
|
196
|
+
"expected": false,
|
|
197
|
+
"description": "leaf 1 rewritten after the older head was taken"
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
}
|