@peac/audit 0.16.1 → 0.16.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/dist/index.d.ts CHANGED
@@ -61,4 +61,6 @@ export { verifyBundle, serializeReport, formatReportText } from './verification-
61
61
  export type { CommerceEvidenceBundle, CommerceSummary, CreateCommerceBundleOptions, LifecycleBundle, LifecycleInputRecord, ProtocolEvidence, TimelineEntry, ObservedAmount, } from './commerce-bundle-types.js';
62
62
  export { COMMERCE_BUNDLE_VERSION, UNCLASSIFIED_LIFECYCLE_BUCKET } from './commerce-bundle-types.js';
63
63
  export { createCommerceEvidenceBundle, addProtocolEvidence, addTimelineEntry, addReceiptRef, computeCommerceSummary, serializeCommerceBundle, groupByLifecycle, } from './commerce-bundle.js';
64
+ export type { Sha256DigestRef, ReceiptMerkleRoot, MerkleNodeHash, ReceiptMerkleCommitment, ReceiptMerkleInclusionProof, MerkleInputErrorCode, } from './merkle.js';
65
+ export { MERKLE_TREE_ALG, MERKLE_HASH_ALG, MerkleInputError, buildReceiptMerkleCommitment, generateReceiptMerkleInclusionProof, verifyReceiptMerkleInclusion, } from './merkle.js';
64
66
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAG9C,YAAY,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,WAAW,EACX,cAAc,EACd,UAAU,EACV,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,cAAc,IAAI,yBAAyB,EAC3C,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG3F,YAAY,EACV,sBAAsB,EACtB,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAEpG,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAG9C,YAAY,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,WAAW,EACX,cAAc,EACd,UAAU,EACV,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,cAAc,IAAI,yBAAyB,EAC3C,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG3F,YAAY,EACV,sBAAsB,EACtB,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAEpG,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,2BAA2B,EAC3B,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,4BAA4B,EAC5B,mCAAmC,EACnC,4BAA4B,GAC7B,MAAM,aAAa,CAAC"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createHash, randomBytes } from 'crypto';
1
+ import { createHash, randomBytes, timingSafeEqual } from 'crypto';
2
2
  import { posix } from 'path';
3
3
  import { canonicalize, verify, CryptoError } from '@peac/crypto';
4
4
  import { BUNDLE_ERRORS } from '@peac/kernel';
@@ -1667,10 +1667,244 @@ function normalizeIat(iat) {
1667
1667
  if (!Number.isNaN(parsed)) return parsed;
1668
1668
  return 0;
1669
1669
  }
1670
+ var MERKLE_TREE_ALG = "peac.merkle.ct-sorted-set-sha256-v1";
1671
+ var MERKLE_HASH_ALG = "sha256";
1672
+ var MerkleInputError = class extends Error {
1673
+ code;
1674
+ constructor(code, message) {
1675
+ super(message);
1676
+ this.name = "MerkleInputError";
1677
+ this.code = code;
1678
+ }
1679
+ };
1680
+ var DIGEST_REF_RE = /^sha256:[0-9a-f]{64}$/;
1681
+ var LEAF_PREFIX = 0;
1682
+ var NODE_PREFIX = 1;
1683
+ function assertDigestRef(value, code, label) {
1684
+ if (typeof value !== "string" || !DIGEST_REF_RE.test(value)) {
1685
+ throw new MerkleInputError(
1686
+ code,
1687
+ `${label} must match sha256:<hex64> (lowercase); received ${describe(value)}`
1688
+ );
1689
+ }
1690
+ }
1691
+ function describe(value) {
1692
+ if (typeof value === "string") return value.length > 74 ? `${value.slice(0, 71)}...` : value;
1693
+ return Object.prototype.toString.call(value);
1694
+ }
1695
+ function decodeDigest(ref) {
1696
+ return Uint8Array.from(Buffer.from(ref.slice("sha256:".length), "hex"));
1697
+ }
1698
+ function encodeDigest(bytes) {
1699
+ return `sha256:${Buffer.from(bytes).toString("hex")}`;
1700
+ }
1701
+ function sha256(...chunks) {
1702
+ const h = createHash("sha256");
1703
+ for (const c of chunks) h.update(c);
1704
+ return Uint8Array.from(h.digest());
1705
+ }
1706
+ function leafHash(digest) {
1707
+ return sha256(Uint8Array.of(LEAF_PREFIX), digest);
1708
+ }
1709
+ function nodeHash(left, right) {
1710
+ return sha256(Uint8Array.of(NODE_PREFIX), left, right);
1711
+ }
1712
+ function largestPow2LessThan(n) {
1713
+ let k = 1;
1714
+ while (k * 2 < n) k *= 2;
1715
+ return k;
1716
+ }
1717
+ function compareBytes(a, b) {
1718
+ const len = Math.min(a.length, b.length);
1719
+ for (let i = 0; i < len; i++) {
1720
+ if (a[i] !== b[i]) return a[i] - b[i];
1721
+ }
1722
+ return a.length - b.length;
1723
+ }
1724
+ function bytesEqual(a, b) {
1725
+ return a.length === b.length && timingSafeEqual(a, b);
1726
+ }
1727
+ function isSafePositiveInteger(value) {
1728
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
1729
+ }
1730
+ function isSafeNonNegativeInteger(value) {
1731
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1732
+ }
1733
+ function isOdd(value) {
1734
+ return value % 2 === 1;
1735
+ }
1736
+ function halve(value) {
1737
+ return Math.floor(value / 2);
1738
+ }
1739
+ function merkleTreeHash(digests, start, end) {
1740
+ const n = end - start;
1741
+ if (n === 1) return leafHash(digests[start]);
1742
+ const k = largestPow2LessThan(n);
1743
+ return nodeHash(
1744
+ merkleTreeHash(digests, start, start + k),
1745
+ merkleTreeHash(digests, start + k, end)
1746
+ );
1747
+ }
1748
+ function inclusionPath(m, digests, start, end) {
1749
+ const n = end - start;
1750
+ if (n === 1) return [];
1751
+ const k = largestPow2LessThan(n);
1752
+ if (m < k) {
1753
+ return [
1754
+ ...inclusionPath(m, digests, start, start + k),
1755
+ merkleTreeHash(digests, start + k, end)
1756
+ ];
1757
+ }
1758
+ return [
1759
+ ...inclusionPath(m - k, digests, start + k, end),
1760
+ merkleTreeHash(digests, start, start + k)
1761
+ ];
1762
+ }
1763
+ function foldInclusionProof(leaf, siblings, leafIndex, treeSize) {
1764
+ if (leafIndex >= treeSize) return null;
1765
+ let fn = leafIndex;
1766
+ let sn = treeSize - 1;
1767
+ let r = leaf;
1768
+ for (const p of siblings) {
1769
+ if (sn === 0) return null;
1770
+ if (isOdd(fn) || fn === sn) {
1771
+ r = nodeHash(p, r);
1772
+ if (!isOdd(fn)) {
1773
+ while (!isOdd(fn) && fn !== 0) {
1774
+ fn = halve(fn);
1775
+ sn = halve(sn);
1776
+ }
1777
+ }
1778
+ } else {
1779
+ r = nodeHash(r, p);
1780
+ }
1781
+ fn = halve(fn);
1782
+ sn = halve(sn);
1783
+ }
1784
+ return sn === 0 ? r : null;
1785
+ }
1786
+ function toSortedDigests(refs) {
1787
+ if (!Array.isArray(refs) || refs.length === 0) {
1788
+ throw new MerkleInputError(
1789
+ "audit.merkle.empty_input",
1790
+ "refs must be a non-empty array of sha256:<hex64> receipt refs"
1791
+ );
1792
+ }
1793
+ const seen = /* @__PURE__ */ new Set();
1794
+ const entries = [];
1795
+ for (const ref of refs) {
1796
+ assertDigestRef(ref, "audit.merkle.invalid_receipt_ref", "receipt_ref");
1797
+ if (seen.has(ref)) {
1798
+ throw new MerkleInputError(
1799
+ "audit.merkle.duplicate_receipt_ref",
1800
+ `duplicate receipt_ref: ${ref}`
1801
+ );
1802
+ }
1803
+ seen.add(ref);
1804
+ entries.push({ ref, digest: decodeDigest(ref) });
1805
+ }
1806
+ entries.sort((a, b) => compareBytes(a.digest, b.digest));
1807
+ return { sorted: entries.map((e) => e.digest), sortedRefs: entries.map((e) => e.ref) };
1808
+ }
1809
+ function buildReceiptMerkleCommitment(refs) {
1810
+ const { sorted } = toSortedDigests(refs);
1811
+ return {
1812
+ tree_alg: MERKLE_TREE_ALG,
1813
+ hash_alg: MERKLE_HASH_ALG,
1814
+ root: encodeDigest(merkleTreeHash(sorted, 0, sorted.length)),
1815
+ tree_size: sorted.length
1816
+ };
1817
+ }
1818
+ function generateReceiptMerkleInclusionProof(refs, targetRef) {
1819
+ assertDigestRef(targetRef, "audit.merkle.invalid_receipt_ref", "targetRef");
1820
+ const { sorted, sortedRefs } = toSortedDigests(refs);
1821
+ const leafIndex = sortedRefs.indexOf(targetRef);
1822
+ if (leafIndex === -1) {
1823
+ throw new MerkleInputError(
1824
+ "audit.merkle.target_not_found",
1825
+ `targetRef not in refs: ${targetRef}`
1826
+ );
1827
+ }
1828
+ return {
1829
+ tree_alg: MERKLE_TREE_ALG,
1830
+ hash_alg: MERKLE_HASH_ALG,
1831
+ leaf_index: leafIndex,
1832
+ tree_size: sorted.length,
1833
+ hashes: inclusionPath(leafIndex, sorted, 0, sorted.length).map(encodeDigest)
1834
+ };
1835
+ }
1836
+ function verifyReceiptMerkleInclusion(commitment, targetRef, proof) {
1837
+ if (commitment === null || typeof commitment !== "object") {
1838
+ throw new MerkleInputError("audit.merkle.invalid_commitment", "commitment must be an object");
1839
+ }
1840
+ if (commitment.tree_alg !== MERKLE_TREE_ALG) {
1841
+ throw new MerkleInputError(
1842
+ "audit.merkle.unsupported_tree_alg",
1843
+ `unsupported commitment tree_alg: ${describe(commitment.tree_alg)}`
1844
+ );
1845
+ }
1846
+ if (commitment.hash_alg !== MERKLE_HASH_ALG) {
1847
+ throw new MerkleInputError(
1848
+ "audit.merkle.unsupported_hash_alg",
1849
+ `unsupported commitment hash_alg: ${describe(commitment.hash_alg)}`
1850
+ );
1851
+ }
1852
+ assertDigestRef(commitment.root, "audit.merkle.invalid_merkle_root", "commitment.root");
1853
+ if (!isSafePositiveInteger(commitment.tree_size)) {
1854
+ throw new MerkleInputError(
1855
+ "audit.merkle.invalid_commitment",
1856
+ `commitment.tree_size must be a positive safe integer; received ${describe(commitment.tree_size)}`
1857
+ );
1858
+ }
1859
+ assertDigestRef(targetRef, "audit.merkle.invalid_receipt_ref", "targetRef");
1860
+ if (proof === null || typeof proof !== "object") {
1861
+ throw new MerkleInputError("audit.merkle.invalid_proof", "proof must be an object");
1862
+ }
1863
+ if (proof.tree_alg !== MERKLE_TREE_ALG) {
1864
+ throw new MerkleInputError(
1865
+ "audit.merkle.unsupported_tree_alg",
1866
+ `unsupported tree_alg: ${describe(proof.tree_alg)}`
1867
+ );
1868
+ }
1869
+ if (proof.hash_alg !== MERKLE_HASH_ALG) {
1870
+ throw new MerkleInputError(
1871
+ "audit.merkle.unsupported_hash_alg",
1872
+ `unsupported hash_alg: ${describe(proof.hash_alg)}`
1873
+ );
1874
+ }
1875
+ if (!isSafePositiveInteger(proof.tree_size)) {
1876
+ throw new MerkleInputError(
1877
+ "audit.merkle.invalid_proof",
1878
+ `tree_size must be a positive safe integer; received ${describe(proof.tree_size)}`
1879
+ );
1880
+ }
1881
+ if (!isSafeNonNegativeInteger(proof.leaf_index) || proof.leaf_index >= proof.tree_size) {
1882
+ throw new MerkleInputError(
1883
+ "audit.merkle.invalid_proof",
1884
+ `leaf_index must be a safe integer in [0, tree_size); received ${describe(proof.leaf_index)}`
1885
+ );
1886
+ }
1887
+ if (!Array.isArray(proof.hashes)) {
1888
+ throw new MerkleInputError("audit.merkle.invalid_proof", "proof.hashes must be an array");
1889
+ }
1890
+ const siblings = [];
1891
+ for (const h of proof.hashes) {
1892
+ assertDigestRef(h, "audit.merkle.invalid_proof_hash", "proof.hashes[]");
1893
+ siblings.push(decodeDigest(h));
1894
+ }
1895
+ if (proof.tree_size !== commitment.tree_size) return false;
1896
+ const recomputed = foldInclusionProof(
1897
+ leafHash(decodeDigest(targetRef)),
1898
+ siblings,
1899
+ proof.leaf_index,
1900
+ proof.tree_size
1901
+ );
1902
+ return recomputed !== null && bytesEqual(recomputed, decodeDigest(commitment.root));
1903
+ }
1670
1904
 
1671
1905
  // src/index.ts
1672
1906
  var AUDIT_PACKAGE_VERSION = "0.9.27";
1673
1907
 
1674
- export { AUDIT_EVENT_TYPES, AUDIT_PACKAGE_VERSION, AUDIT_SEVERITIES, AUDIT_VERSION, BUNDLE_VERSION, COMMERCE_BUNDLE_VERSION, DISPUTE_BUNDLE_VERSION, BUNDLE_VERSION2 as DISPUTE_BUNDLE_VERSION_v2, UNCLASSIFIED_LIFECYCLE_BUCKET, VERIFICATION_REPORT_VERSION, addProtocolEvidence, addReceiptRef, addTimelineEntry, computeCommerceSummary, correlateByTrace, createAuditEntry, createCaseBundle, createCommerceEvidenceBundle, createDisputeBundle, createJsonlAppender, filterByDispute, filterByResource, filterByTimeRange, filterByTraceId, formatJsonl, formatJsonlLine, formatReportText, generateAuditId, generateBundleSummary, getBundleContentHash, groupByLifecycle, isValidAuditEntry, isValidTraceContext, isValidUlid, parseJsonl, parseJsonlLine, readDisputeBundle, serializeBundle, serializeCommerceBundle, serializeReport, validateAuditEntry, verifyBundle, verifyBundleIntegrity };
1908
+ export { AUDIT_EVENT_TYPES, AUDIT_PACKAGE_VERSION, AUDIT_SEVERITIES, AUDIT_VERSION, BUNDLE_VERSION, COMMERCE_BUNDLE_VERSION, DISPUTE_BUNDLE_VERSION, BUNDLE_VERSION2 as DISPUTE_BUNDLE_VERSION_v2, MERKLE_HASH_ALG, MERKLE_TREE_ALG, MerkleInputError, UNCLASSIFIED_LIFECYCLE_BUCKET, VERIFICATION_REPORT_VERSION, addProtocolEvidence, addReceiptRef, addTimelineEntry, buildReceiptMerkleCommitment, computeCommerceSummary, correlateByTrace, createAuditEntry, createCaseBundle, createCommerceEvidenceBundle, createDisputeBundle, createJsonlAppender, filterByDispute, filterByResource, filterByTimeRange, filterByTraceId, formatJsonl, formatJsonlLine, formatReportText, generateAuditId, generateBundleSummary, generateReceiptMerkleInclusionProof, getBundleContentHash, groupByLifecycle, isValidAuditEntry, isValidTraceContext, isValidUlid, parseJsonl, parseJsonlLine, readDisputeBundle, serializeBundle, serializeCommerceBundle, serializeReport, validateAuditEntry, verifyBundle, verifyBundleIntegrity, verifyReceiptMerkleInclusion };
1675
1909
  //# sourceMappingURL=index.mjs.map
1676
1910
  //# sourceMappingURL=index.mjs.map