@mikeargento/bitgraph 1.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 +9 -0
- package/NOTICE +14 -0
- package/README.md +175 -0
- package/dist/__tests__/canonical.test.d.ts +2 -0
- package/dist/__tests__/canonical.test.d.ts.map +1 -0
- package/dist/__tests__/canonical.test.js +162 -0
- package/dist/__tests__/canonical.test.js.map +1 -0
- package/dist/__tests__/commit-service.integration.test.d.ts +2 -0
- package/dist/__tests__/commit-service.integration.test.d.ts.map +1 -0
- package/dist/__tests__/commit-service.integration.test.js +95 -0
- package/dist/__tests__/commit-service.integration.test.js.map +1 -0
- package/dist/__tests__/constructor.test.d.ts +2 -0
- package/dist/__tests__/constructor.test.d.ts.map +1 -0
- package/dist/__tests__/constructor.test.js +267 -0
- package/dist/__tests__/constructor.test.js.map +1 -0
- package/dist/__tests__/hello-world.test.d.ts +2 -0
- package/dist/__tests__/hello-world.test.d.ts.map +1 -0
- package/dist/__tests__/hello-world.test.js +9 -0
- package/dist/__tests__/hello-world.test.js.map +1 -0
- package/dist/__tests__/policy-enforcement.test.d.ts +2 -0
- package/dist/__tests__/policy-enforcement.test.d.ts.map +1 -0
- package/dist/__tests__/policy-enforcement.test.js +188 -0
- package/dist/__tests__/policy-enforcement.test.js.map +1 -0
- package/dist/__tests__/proof-hash-regression.test.d.ts +2 -0
- package/dist/__tests__/proof-hash-regression.test.d.ts.map +1 -0
- package/dist/__tests__/proof-hash-regression.test.js +134 -0
- package/dist/__tests__/proof-hash-regression.test.js.map +1 -0
- package/dist/__tests__/proof-hash.test.d.ts +2 -0
- package/dist/__tests__/proof-hash.test.d.ts.map +1 -0
- package/dist/__tests__/proof-hash.test.js +134 -0
- package/dist/__tests__/proof-hash.test.js.map +1 -0
- package/dist/__tests__/verifier.test.d.ts +2 -0
- package/dist/__tests__/verifier.test.d.ts.map +1 -0
- package/dist/__tests__/verifier.test.js +457 -0
- package/dist/__tests__/verifier.test.js.map +1 -0
- package/dist/canonical.d.ts +54 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +119 -0
- package/dist/canonical.js.map +1 -0
- package/dist/constructor.d.ts +66 -0
- package/dist/constructor.d.ts.map +1 -0
- package/dist/constructor.js +339 -0
- package/dist/constructor.js.map +1 -0
- package/dist/host.d.ts +138 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +3 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +68 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +176 -0
- package/dist/policy.js.map +1 -0
- package/dist/proof-hash.d.ts +9 -0
- package/dist/proof-hash.d.ts.map +1 -0
- package/dist/proof-hash.js +75 -0
- package/dist/proof-hash.js.map +1 -0
- package/dist/types.d.ts +705 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/verifier.d.ts +27 -0
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +844 -0
- package/dist/verifier.js.map +1 -0
- package/package.json +40 -0
- package/src/__tests__/canonical.test.ts +253 -0
- package/src/__tests__/commit-service.integration.test.ts +117 -0
- package/src/__tests__/constructor.test.ts +340 -0
- package/src/__tests__/hello-world.test.ts +10 -0
- package/src/__tests__/policy-enforcement.test.ts +226 -0
- package/src/__tests__/proof-hash-regression.test.ts +147 -0
- package/src/__tests__/proof-hash.test.ts +148 -0
- package/src/__tests__/verifier.test.ts +541 -0
- package/src/constructor.ts +435 -0
- package/src/host.ts +153 -0
- package/src/index.ts +51 -0
- package/src/policy.ts +239 -0
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { PolicyBinding } from "@mikeargento/bitgraph-verify";
|
|
2
|
+
export interface PolicyDocument {
|
|
3
|
+
/** Human-readable policy name (from "# Policy: <name>"). */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Version identifier (from "version: <version>"). */
|
|
6
|
+
version: string;
|
|
7
|
+
/** Structured rules parsed from the document. */
|
|
8
|
+
rules: PolicyRules;
|
|
9
|
+
/** The original raw markdown text. */
|
|
10
|
+
raw: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PolicyRules {
|
|
13
|
+
/** Always true — BitGraph is default-deny. */
|
|
14
|
+
defaultDeny: true;
|
|
15
|
+
/** Tool names explicitly allowed by the policy. */
|
|
16
|
+
allowedTools?: string[] | undefined;
|
|
17
|
+
/** Maximum total actions permitted under this policy. */
|
|
18
|
+
maxActions?: number | undefined;
|
|
19
|
+
/** Maximum actions per minute. */
|
|
20
|
+
rateLimit?: number | undefined;
|
|
21
|
+
/** Allowed hours of operation (24h clock, 0-23). */
|
|
22
|
+
timeWindow?: {
|
|
23
|
+
start: number;
|
|
24
|
+
end: number;
|
|
25
|
+
} | undefined;
|
|
26
|
+
/** Tool names that require multi-sig / human approval. */
|
|
27
|
+
requireApproval?: string[] | undefined;
|
|
28
|
+
/** Arbitrary extension metadata. */
|
|
29
|
+
metadata?: Record<string, unknown> | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a markdown policy document into structured rules.
|
|
33
|
+
*
|
|
34
|
+
* Extracts the policy name, version, and rule sections from a
|
|
35
|
+
* human-readable markdown format.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parsePolicy(markdown: string): PolicyDocument;
|
|
38
|
+
/**
|
|
39
|
+
* Compute the SHA-256 hash of a policy document's raw text.
|
|
40
|
+
*
|
|
41
|
+
* Returns a Base64-standard (RFC 4648 §4) encoded digest suitable for
|
|
42
|
+
* inclusion in the proof's policy.digestB64 field.
|
|
43
|
+
*/
|
|
44
|
+
export declare function hashPolicy(markdown: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Create a PolicyBinding from a raw markdown policy document.
|
|
47
|
+
*
|
|
48
|
+
* Convenience function that parses the document for name/version and
|
|
49
|
+
* computes the SHA-256 digest in one call.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createPolicyBinding(markdown: string): PolicyBinding;
|
|
52
|
+
export interface ActionValidationResult {
|
|
53
|
+
/** Whether the action is permitted under the policy rules. */
|
|
54
|
+
valid: boolean;
|
|
55
|
+
/** Human-readable reason when valid is false. */
|
|
56
|
+
reason?: string | undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate that an action complies with the policy rules.
|
|
60
|
+
*
|
|
61
|
+
* Checks tool allowlist and time window constraints. Rate limiting
|
|
62
|
+
* and action counting require external state and are not checked here.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateAction(action: {
|
|
65
|
+
tool: string;
|
|
66
|
+
timestamp?: number | undefined;
|
|
67
|
+
}, rules: PolicyRules): ActionValidationResult;
|
|
68
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAMlE,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,EAAE,WAAW,CAAC;IACnB,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,WAAW,EAAE,IAAI,CAAC;IAClB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACpC,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,oDAAoD;IACpD,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACxD,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACvC,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAChD;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAoD5D;AA+BD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAInD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAQnE;AAMD,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EACxD,KAAK,EAAE,WAAW,GACjB,sBAAsB,CAiCxB"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Copyright (c) Mike Argento. All rights reserved. See LICENSE.
|
|
2
|
+
/**
|
|
3
|
+
* bitgraph-core Policy — parse, hash, and validate markdown policy documents.
|
|
4
|
+
*
|
|
5
|
+
* Policy documents are human-readable markdown files that define what an
|
|
6
|
+
* agent is allowed to do. The SHA-256 hash of the raw document bytes is
|
|
7
|
+
* included in the proof's signed body, cryptographically binding the
|
|
8
|
+
* proof to the exact policy that governed the action.
|
|
9
|
+
*
|
|
10
|
+
* Format:
|
|
11
|
+
* # Policy: <name>
|
|
12
|
+
* version: <version>
|
|
13
|
+
*
|
|
14
|
+
* ## Allowed Tools
|
|
15
|
+
* - tool_a
|
|
16
|
+
* - tool_b
|
|
17
|
+
*
|
|
18
|
+
* ## Limits
|
|
19
|
+
* - max_actions: 500
|
|
20
|
+
* - rate_limit: 10/min
|
|
21
|
+
*
|
|
22
|
+
* ## Time Window
|
|
23
|
+
* - hours: 9-17
|
|
24
|
+
*
|
|
25
|
+
* ## Require Approval
|
|
26
|
+
* - send_email
|
|
27
|
+
*/
|
|
28
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Parsing
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Parse a markdown policy document into structured rules.
|
|
34
|
+
*
|
|
35
|
+
* Extracts the policy name, version, and rule sections from a
|
|
36
|
+
* human-readable markdown format.
|
|
37
|
+
*/
|
|
38
|
+
export function parsePolicy(markdown) {
|
|
39
|
+
const lines = markdown.split("\n");
|
|
40
|
+
const name = extractField(lines, /^#\s+Policy:\s*(.+)/);
|
|
41
|
+
const version = extractField(lines, /^version:\s*(.+)/);
|
|
42
|
+
const rules = { defaultDeny: true };
|
|
43
|
+
// Parse allowed tools section
|
|
44
|
+
const toolsSection = extractSection(lines, "Allowed Tools");
|
|
45
|
+
if (toolsSection.length > 0) {
|
|
46
|
+
rules.allowedTools = toolsSection
|
|
47
|
+
.map((line) => line.replace(/^[-*]\s*/, "").trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
// Parse limits
|
|
51
|
+
const limitsSection = extractSection(lines, "Limits");
|
|
52
|
+
for (const line of limitsSection) {
|
|
53
|
+
const clean = line.replace(/^[-*]\s*/, "").trim();
|
|
54
|
+
const maxMatch = clean.match(/max_actions:\s*(\d+)/);
|
|
55
|
+
if (maxMatch?.[1])
|
|
56
|
+
rules.maxActions = parseInt(maxMatch[1], 10);
|
|
57
|
+
const rateMatch = clean.match(/rate_limit:\s*(\d+)/);
|
|
58
|
+
if (rateMatch?.[1])
|
|
59
|
+
rules.rateLimit = parseInt(rateMatch[1], 10);
|
|
60
|
+
}
|
|
61
|
+
// Parse time window
|
|
62
|
+
const timeSection = extractSection(lines, "Time Window");
|
|
63
|
+
for (const line of timeSection) {
|
|
64
|
+
const clean = line.replace(/^[-*]\s*/, "").trim();
|
|
65
|
+
const hoursMatch = clean.match(/hours:\s*(\d+)-(\d+)/);
|
|
66
|
+
if (hoursMatch?.[1] && hoursMatch[2]) {
|
|
67
|
+
rules.timeWindow = {
|
|
68
|
+
start: parseInt(hoursMatch[1], 10),
|
|
69
|
+
end: parseInt(hoursMatch[2], 10),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Parse require approval
|
|
74
|
+
const approvalSection = extractSection(lines, "Require Approval");
|
|
75
|
+
if (approvalSection.length > 0) {
|
|
76
|
+
rules.requireApproval = approvalSection
|
|
77
|
+
.map((line) => line.replace(/^[-*]\s*/, "").trim())
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
name: name ?? "Unnamed Policy",
|
|
82
|
+
version: version ?? "1.0",
|
|
83
|
+
rules,
|
|
84
|
+
raw: markdown,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function extractField(lines, pattern) {
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
const match = line.match(pattern);
|
|
90
|
+
if (match?.[1])
|
|
91
|
+
return match[1].trim();
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
function extractSection(lines, sectionName) {
|
|
96
|
+
const result = [];
|
|
97
|
+
let inSection = false;
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
if (line.match(new RegExp(`^##\\s+${sectionName}`, "i"))) {
|
|
100
|
+
inSection = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (inSection && line.match(/^##\s+/))
|
|
104
|
+
break; // next section
|
|
105
|
+
if (inSection && line.trim())
|
|
106
|
+
result.push(line);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Hashing
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
/**
|
|
114
|
+
* Compute the SHA-256 hash of a policy document's raw text.
|
|
115
|
+
*
|
|
116
|
+
* Returns a Base64-standard (RFC 4648 §4) encoded digest suitable for
|
|
117
|
+
* inclusion in the proof's policy.digestB64 field.
|
|
118
|
+
*/
|
|
119
|
+
export function hashPolicy(markdown) {
|
|
120
|
+
const bytes = new TextEncoder().encode(markdown);
|
|
121
|
+
const hash = sha256(bytes);
|
|
122
|
+
return Buffer.from(hash).toString("base64");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a PolicyBinding from a raw markdown policy document.
|
|
126
|
+
*
|
|
127
|
+
* Convenience function that parses the document for name/version and
|
|
128
|
+
* computes the SHA-256 digest in one call.
|
|
129
|
+
*/
|
|
130
|
+
export function createPolicyBinding(markdown) {
|
|
131
|
+
const doc = parsePolicy(markdown);
|
|
132
|
+
const binding = {
|
|
133
|
+
digestB64: hashPolicy(markdown),
|
|
134
|
+
};
|
|
135
|
+
if (doc.name !== "Unnamed Policy")
|
|
136
|
+
binding.name = doc.name;
|
|
137
|
+
if (doc.version !== "1.0")
|
|
138
|
+
binding.version = doc.version;
|
|
139
|
+
return binding;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Validate that an action complies with the policy rules.
|
|
143
|
+
*
|
|
144
|
+
* Checks tool allowlist and time window constraints. Rate limiting
|
|
145
|
+
* and action counting require external state and are not checked here.
|
|
146
|
+
*/
|
|
147
|
+
export function validateAction(action, rules) {
|
|
148
|
+
// Default deny — tool must be in allowed list
|
|
149
|
+
if (rules.allowedTools && !rules.allowedTools.includes(action.tool)) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
reason: `Tool "${action.tool}" not in allowed list`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Time window check
|
|
156
|
+
if (rules.timeWindow && action.timestamp !== undefined) {
|
|
157
|
+
const hour = new Date(action.timestamp).getHours();
|
|
158
|
+
if (hour < rules.timeWindow.start || hour >= rules.timeWindow.end) {
|
|
159
|
+
return {
|
|
160
|
+
valid: false,
|
|
161
|
+
reason: `Action outside allowed hours (${rules.timeWindow.start}-${rules.timeWindow.end})`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Require approval check (advisory — returns valid but flags the tool)
|
|
166
|
+
// Actual approval enforcement happens at the proxy layer
|
|
167
|
+
if (rules.requireApproval &&
|
|
168
|
+
rules.requireApproval.includes(action.tool)) {
|
|
169
|
+
return {
|
|
170
|
+
valid: true,
|
|
171
|
+
reason: `Tool "${action.tool}" requires approval`,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return { valid: true };
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BitGraphProof } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the canonical hash of a BitGraph proof's signed body.
|
|
4
|
+
*
|
|
5
|
+
* @param proof - The full BitGraphProof object (or equivalent Record)
|
|
6
|
+
* @returns Base64-standard encoded SHA-256 hash
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeProofHash(proof: BitGraphProof | Record<string, unknown>): string;
|
|
9
|
+
//# sourceMappingURL=proof-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-hash.d.ts","sourceRoot":"","sources":["../src/proof-hash.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAqCvF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright (c) Mike Argento. All rights reserved. See LICENSE.
|
|
2
|
+
/**
|
|
3
|
+
* bitgraph-core proof hash
|
|
4
|
+
*
|
|
5
|
+
* Canonical, deterministic proof hash computation.
|
|
6
|
+
*
|
|
7
|
+
* The proof hash covers the SIGNED BODY — the fields that are
|
|
8
|
+
* cryptographically signed by the enclave. This matches what the
|
|
9
|
+
* Ed25519 signature covers, making the hash verifiable.
|
|
10
|
+
*
|
|
11
|
+
* Signed body fields:
|
|
12
|
+
* - version, artifact, commit
|
|
13
|
+
* - publicKeyB64 (from signer)
|
|
14
|
+
* - enforcement, measurement (from environment)
|
|
15
|
+
* - attribution (if present)
|
|
16
|
+
* - attestationFormat (if attestation present)
|
|
17
|
+
*
|
|
18
|
+
* This hash is used for:
|
|
19
|
+
* - prevB64 chain linking
|
|
20
|
+
* - S3 ledger key generation
|
|
21
|
+
* - Ethereum anchor binding
|
|
22
|
+
* - Proof deduplication and verification
|
|
23
|
+
*
|
|
24
|
+
* Algorithm:
|
|
25
|
+
* 1. Extract signed body fields
|
|
26
|
+
* 2. canonicalize(signedBody) — recursive key sort, compact JSON, UTF-8
|
|
27
|
+
* 3. SHA-256 of canonical bytes
|
|
28
|
+
* 4. Base64-standard encode (RFC 4648 §4)
|
|
29
|
+
*
|
|
30
|
+
* IMPORTANT: All call sites MUST use this function. Do NOT compute
|
|
31
|
+
* proof hashes ad-hoc with JSON.stringify or Object.keys().sort().
|
|
32
|
+
* Non-recursive key sorting diverges on nested objects.
|
|
33
|
+
*/
|
|
34
|
+
import { canonicalize } from "./canonical.js";
|
|
35
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
36
|
+
/**
|
|
37
|
+
* Compute the canonical hash of a BitGraph proof's signed body.
|
|
38
|
+
*
|
|
39
|
+
* @param proof - The full BitGraphProof object (or equivalent Record)
|
|
40
|
+
* @returns Base64-standard encoded SHA-256 hash
|
|
41
|
+
*/
|
|
42
|
+
export function computeProofHash(proof) {
|
|
43
|
+
const p = proof;
|
|
44
|
+
const signer = p.signer;
|
|
45
|
+
const env = p.environment;
|
|
46
|
+
const signedBody = {
|
|
47
|
+
version: p.version,
|
|
48
|
+
artifact: p.artifact,
|
|
49
|
+
commit: p.commit,
|
|
50
|
+
publicKeyB64: signer?.publicKeyB64,
|
|
51
|
+
enforcement: env?.enforcement,
|
|
52
|
+
measurement: env?.measurement,
|
|
53
|
+
};
|
|
54
|
+
// Include attribution if present
|
|
55
|
+
if (p.attribution) {
|
|
56
|
+
signedBody.attribution = p.attribution;
|
|
57
|
+
}
|
|
58
|
+
// Include attestation format if present
|
|
59
|
+
if (env?.attestation) {
|
|
60
|
+
signedBody.attestationFormat = env.attestation.format;
|
|
61
|
+
}
|
|
62
|
+
const bytes = canonicalize(signedBody);
|
|
63
|
+
const hash = sha256(bytes);
|
|
64
|
+
// Base64 encode (works in both Node.js and browser)
|
|
65
|
+
if (typeof Buffer !== "undefined") {
|
|
66
|
+
return Buffer.from(hash).toString("base64");
|
|
67
|
+
}
|
|
68
|
+
// Browser fallback
|
|
69
|
+
let binary = "";
|
|
70
|
+
for (let i = 0; i < hash.length; i++) {
|
|
71
|
+
binary += String.fromCharCode(hash[i]);
|
|
72
|
+
}
|
|
73
|
+
return btoa(binary);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=proof-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-hash.js","sourceRoot":"","sources":["../src/proof-hash.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAG9C;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA8C;IAC7E,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,MAA8C,CAAC;IAChE,MAAM,GAAG,GAAG,CAAC,CAAC,WAAyG,CAAC;IAExH,MAAM,UAAU,GAA4B;QAC1C,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,YAAY,EAAE,MAAM,EAAE,YAAY;QAClC,WAAW,EAAE,GAAG,EAAE,WAAW;QAC7B,WAAW,EAAE,GAAG,EAAE,WAAW;KAC9B,CAAC;IAEF,iCAAiC;IACjC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClB,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IACzC,CAAC;IAED,wCAAwC;IACxC,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC;QACrB,UAAU,CAAC,iBAAiB,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC;IACxD,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,UAAsC,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE3B,oDAAoD;IACpD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IACD,mBAAmB;IACnB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
|