@typedstandards/verify-core 0.15.0 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/profiles.d.ts +3 -3
- package/dist/profiles.js +5 -5
- package/dist/rekor-binding.d.ts +12 -0
- package/dist/rekor-binding.js +48 -0
- package/dist/rekor-inclusion.d.ts +6 -2
- package/dist/rekor-inclusion.js +3 -1
- package/dist/rekor.d.ts +2 -1
- package/dist/rekor.js +5 -3
- package/dist/verify.d.ts +2 -1
- package/dist/verify.js +3 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,40 @@ references are to `npstorey/civic-ai-tools-website` (#119 is the offline-crypto
|
|
|
6
6
|
hardening arc; #116 is the standalone-verifier arc this package was extracted
|
|
7
7
|
in).
|
|
8
8
|
|
|
9
|
+
## 0.15.2 — 2026-11-07
|
|
10
|
+
|
|
11
|
+
The S2-A patch (anchor npstorey/civic-ai-tools#309). **A patch**: no export, signature or type
|
|
12
|
+
changes.
|
|
13
|
+
|
|
14
|
+
- **The `ai-assisted-analysis/civicaitools-v1` row pins revision 3 of its guidance bundle**
|
|
15
|
+
(2026-11-07), as hub ADR-0038's amendment of 2026-11-07 records:
|
|
16
|
+
`https://raw.githubusercontent.com/npstorey/civic-ai-tools/v0.1.15-typed-standards-spec/docs/profiles/ai-assisted-analysis/civicaitools-v1/bundle.json`,
|
|
17
|
+
SHA-256 `db8175703601a5de9d785a2cda93db194eb05cdeeb222dcfd509ea8ab249f016`. The row's key and
|
|
18
|
+
`admits` are unchanged. The local copy (not published) is byte-identical to the hub file at
|
|
19
|
+
tree `d958f067`; its provenance is in `src/__fixtures__/README.md`.
|
|
20
|
+
|
|
21
|
+
## 0.15.1 — 2026-11-02
|
|
22
|
+
|
|
23
|
+
The R11 patch (npstorey/civic-ai-tools#293; anchor npstorey/civic-ai-tools#294). **A patch**: no
|
|
24
|
+
export, signature or type changes.
|
|
25
|
+
|
|
26
|
+
- **Check #8's inclusion verdict requires the log entry to record this record's hash.**
|
|
27
|
+
`rekorInclusion.inclusionVerified` and `checkpointVerified` are true only when the entry body's
|
|
28
|
+
`spec.data.hash` is `sha512` with the value `rekorHashForPackage(packageHash)`. That holds for an
|
|
29
|
+
entry body and proof the input carries (`verifyRecord`) and for an entry `verifyRekorEntry`
|
|
30
|
+
fetches (its `inclusion`). An entry that does not record the hash, or an empty `packageHash`,
|
|
31
|
+
reads both false, with the proof's `treeSize` and `leafIndex` and no `reason`.
|
|
32
|
+
`verifyRekorInclusion` is unchanged: it checks inclusion alone.
|
|
33
|
+
- **Every genuine pairing reads as before.** Over the package objects and the carried log
|
|
34
|
+
inclusions in this repository's fixtures, and two host-served commitment views, `verifyRecord`
|
|
35
|
+
gives output deep-equal to 0.15.0's, with the entry carried, fetched, or both.
|
|
36
|
+
- **The `ai-assisted-analysis/civicaitools-v1` row pins revision 2 of its guidance bundle**
|
|
37
|
+
(2026-11-02), as hub ADR-0038's amendment of 2026-11-02 records:
|
|
38
|
+
`https://raw.githubusercontent.com/npstorey/civic-ai-tools/v0.1.14-typed-standards-spec/docs/profiles/ai-assisted-analysis/civicaitools-v1/bundle.json`,
|
|
39
|
+
SHA-256 `45bf3ab479325a791829ed9987f5f291fea44238b54bfb598f32f306f574eb9b`. The row's key and
|
|
40
|
+
`admits` are unchanged. The local copy (not published) is byte-identical to the hub file at
|
|
41
|
+
tree `bcd06c5f`; its provenance is in `src/__fixtures__/README.md`.
|
|
42
|
+
|
|
9
43
|
## 0.15.0 — 2026-10-30
|
|
10
44
|
|
|
11
45
|
The R10 release (civic-ai-tools#289; hub ADR-0038 §3, §5 and §6, specification v0.1.13 §8.1.1,
|
package/dist/profiles.d.ts
CHANGED
|
@@ -33,9 +33,9 @@ export interface ProducerProfileSubtypeRow {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* The subtype table, keyed by the full `producerProfile` value. The
|
|
36
|
-
* `ai-assisted-analysis/civicaitools-v1` bundle is revision
|
|
37
|
-
* hub ADR-0038
|
|
38
|
-
* digest and this pin, and nothing else.
|
|
36
|
+
* `ai-assisted-analysis/civicaitools-v1` bundle is revision 3 (2026-11-07), as
|
|
37
|
+
* hub ADR-0038's amendment of 2026-11-07 records its address and digest; a later
|
|
38
|
+
* revision moves the digest and this pin, and nothing else.
|
|
39
39
|
*/
|
|
40
40
|
export declare const PRODUCER_PROFILE_SUBTYPES: Readonly<Record<string, ProducerProfileSubtypeRow>>;
|
|
41
41
|
/**
|
package/dist/profiles.js
CHANGED
|
@@ -45,9 +45,9 @@ export function captureVocabForProfile(producerProfile, contentProfile) {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* The subtype table, keyed by the full `producerProfile` value. The
|
|
48
|
-
* `ai-assisted-analysis/civicaitools-v1` bundle is revision
|
|
49
|
-
* hub ADR-0038
|
|
50
|
-
* digest and this pin, and nothing else.
|
|
48
|
+
* `ai-assisted-analysis/civicaitools-v1` bundle is revision 3 (2026-11-07), as
|
|
49
|
+
* hub ADR-0038's amendment of 2026-11-07 records its address and digest; a later
|
|
50
|
+
* revision moves the digest and this pin, and nothing else.
|
|
51
51
|
*/
|
|
52
52
|
export const PRODUCER_PROFILE_SUBTYPES = Object.freeze({
|
|
53
53
|
// No bundle pinned: the integration partner's, not this table's to name.
|
|
@@ -55,8 +55,8 @@ export const PRODUCER_PROFILE_SUBTYPES = Object.freeze({
|
|
|
55
55
|
'ai-assisted-analysis/civicaitools-v1': Object.freeze({
|
|
56
56
|
admits: Object.freeze(['datHere', 'default']),
|
|
57
57
|
bundle: Object.freeze({
|
|
58
|
-
url: 'https://raw.githubusercontent.com/npstorey/civic-ai-tools/v0.1.
|
|
59
|
-
sha256: '
|
|
58
|
+
url: 'https://raw.githubusercontent.com/npstorey/civic-ai-tools/v0.1.15-typed-standards-spec/docs/profiles/ai-assisted-analysis/civicaitools-v1/bundle.json',
|
|
59
|
+
sha256: 'db8175703601a5de9d785a2cda93db194eb05cdeeb222dcfd509ea8ab249f016',
|
|
60
60
|
}),
|
|
61
61
|
}),
|
|
62
62
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type RekorInclusionProof, type RekorInclusionResult } from './rekor-inclusion.ts';
|
|
2
|
+
/** Whether a log entry body records `packageHash`: `spec.data.hash` is `sha512`
|
|
3
|
+
* with the value `rekorHashForPackage(packageHash)`. An empty hash, or a body that
|
|
4
|
+
* does not decode, records nothing. */
|
|
5
|
+
export declare function entryRecordsPackage(entryBody: string | Uint8Array, packageHash: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Check 8's inclusion verdict for a record. An entry that does not record
|
|
8
|
+
* `packageHash` reads `inclusionVerified: false` and `checkpointVerified: false`,
|
|
9
|
+
* with the proof's `treeSize` and `leafIndex` and no `reason` (the proof is not
|
|
10
|
+
* evaluated). Otherwise the verdict is `verifyRekorInclusion`'s.
|
|
11
|
+
*/
|
|
12
|
+
export declare function verifyRekorInclusionForRecord(entryBody: string | Uint8Array, inclusionProof: RekorInclusionProof, packageHash: string): RekorInclusionResult;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Check 8's inclusion verdict, bound to the record (spec §9.2 check #8) —
|
|
2
|
+
// browser-safe, offline.
|
|
3
|
+
//
|
|
4
|
+
// `verifyRekorInclusion` says whether a log entry is in a log the pinned key
|
|
5
|
+
// vouches for. A verdict about a record also needs the entry to be that record's:
|
|
6
|
+
// its body's `spec.data.hash` is `sha512` with the value
|
|
7
|
+
// `rekorHashForPackage(packageHash)`, the comparison `verifyRekorEntry` makes for
|
|
8
|
+
// hash parity. Both sources of a verdict read it here: the entry body and proof an
|
|
9
|
+
// input carries (`verifyRecord`) and an entry `verifyRekorEntry` fetches.
|
|
10
|
+
//
|
|
11
|
+
// Internal: `index.ts` does not re-export this module.
|
|
12
|
+
import { base64ToBytes } from "./primitives.js";
|
|
13
|
+
import { rekorHashForPackage } from "./signature.js";
|
|
14
|
+
import { verifyRekorInclusion, } from "./rekor-inclusion.js";
|
|
15
|
+
/** Whether a log entry body records `packageHash`: `spec.data.hash` is `sha512`
|
|
16
|
+
* with the value `rekorHashForPackage(packageHash)`. An empty hash, or a body that
|
|
17
|
+
* does not decode, records nothing. */
|
|
18
|
+
export function entryRecordsPackage(entryBody, packageHash) {
|
|
19
|
+
if (!packageHash)
|
|
20
|
+
return false;
|
|
21
|
+
try {
|
|
22
|
+
const bytes = typeof entryBody === 'string' ? base64ToBytes(entryBody) : entryBody;
|
|
23
|
+
const body = JSON.parse(new TextDecoder().decode(bytes));
|
|
24
|
+
const hash = body
|
|
25
|
+
?.spec?.data?.hash;
|
|
26
|
+
return hash?.algorithm === 'sha512' && hash.value === rekorHashForPackage(packageHash);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check 8's inclusion verdict for a record. An entry that does not record
|
|
34
|
+
* `packageHash` reads `inclusionVerified: false` and `checkpointVerified: false`,
|
|
35
|
+
* with the proof's `treeSize` and `leafIndex` and no `reason` (the proof is not
|
|
36
|
+
* evaluated). Otherwise the verdict is `verifyRekorInclusion`'s.
|
|
37
|
+
*/
|
|
38
|
+
export function verifyRekorInclusionForRecord(entryBody, inclusionProof, packageHash) {
|
|
39
|
+
if (!entryRecordsPackage(entryBody, packageHash)) {
|
|
40
|
+
return {
|
|
41
|
+
inclusionVerified: false,
|
|
42
|
+
checkpointVerified: false,
|
|
43
|
+
treeSize: inclusionProof.treeSize,
|
|
44
|
+
leafIndex: inclusionProof.logIndex,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return verifyRekorInclusion(entryBody, inclusionProof);
|
|
48
|
+
}
|
|
@@ -53,7 +53,9 @@ export interface RekorInclusionResult {
|
|
|
53
53
|
origin?: string;
|
|
54
54
|
/** The matched anchor's key hint (hex), for display / provenance. */
|
|
55
55
|
keyHint?: string;
|
|
56
|
-
/** The first thing that failed, when not fully verified.
|
|
56
|
+
/** The first thing that failed, when not fully verified. Absent when
|
|
57
|
+
* `verifyRecord` or `verifyRekorEntry` found that the entry does not record the
|
|
58
|
+
* package's hash: the proof is then not evaluated. */
|
|
57
59
|
reason?: RekorInclusionFailReason;
|
|
58
60
|
}
|
|
59
61
|
/**
|
|
@@ -103,7 +105,9 @@ export declare function verifyCheckpointSignature(checkpoint: ParsedCheckpoint,
|
|
|
103
105
|
* is the carried `verification.inclusionProof`. Returns a graded verdict:
|
|
104
106
|
* `inclusionVerified` (the proof folds to its root) and `checkpointVerified` (that
|
|
105
107
|
* root is in a checkpoint a pinned log key signed). The strong property — "this
|
|
106
|
-
* entry is in a log the sigstore key vouches for" — is both being true.
|
|
108
|
+
* entry is in a log the sigstore key vouches for" — is both being true. This
|
|
109
|
+
* export checks inclusion alone; `verifyRecord` (`verifyEvidence`) applies the
|
|
110
|
+
* record binding.
|
|
107
111
|
*/
|
|
108
112
|
export declare function verifyRekorInclusion(entryBody: string | Uint8Array, inclusionProof: RekorInclusionProof, anchors?: readonly RekorLogAnchor[]): RekorInclusionResult;
|
|
109
113
|
export {};
|
package/dist/rekor-inclusion.js
CHANGED
|
@@ -239,7 +239,9 @@ export function verifyCheckpointSignature(checkpoint, anchors) {
|
|
|
239
239
|
* is the carried `verification.inclusionProof`. Returns a graded verdict:
|
|
240
240
|
* `inclusionVerified` (the proof folds to its root) and `checkpointVerified` (that
|
|
241
241
|
* root is in a checkpoint a pinned log key signed). The strong property — "this
|
|
242
|
-
* entry is in a log the sigstore key vouches for" — is both being true.
|
|
242
|
+
* entry is in a log the sigstore key vouches for" — is both being true. This
|
|
243
|
+
* export checks inclusion alone; `verifyRecord` (`verifyEvidence`) applies the
|
|
244
|
+
* record binding.
|
|
243
245
|
*/
|
|
244
246
|
export function verifyRekorInclusion(entryBody, inclusionProof, anchors = REKOR_LOG_ANCHORS) {
|
|
245
247
|
const base = {
|
package/dist/rekor.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export interface RekorVerifyResult {
|
|
|
7
7
|
integratedTime?: number;
|
|
8
8
|
logEntryUrl?: string;
|
|
9
9
|
/** Cryptographic Merkle-inclusion verdict (#119), when the fetched entry carries
|
|
10
|
-
* an inclusion proof
|
|
10
|
+
* an inclusion proof; not verified when the entry does not record this package's
|
|
11
|
+
* hash. Additive — `verified` (hash-parity) is unchanged. */
|
|
11
12
|
inclusion?: RekorInclusionResult;
|
|
12
13
|
}
|
|
13
14
|
/**
|
package/dist/rekor.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// instead of `Buffer`, and the network fetch is injected.
|
|
10
10
|
import { rekorHashForPackage } from "./signature.js";
|
|
11
11
|
import { base64ToBytes } from "./primitives.js";
|
|
12
|
-
import {
|
|
12
|
+
import { verifyRekorInclusionForRecord } from "./rekor-binding.js";
|
|
13
13
|
/**
|
|
14
14
|
* Verify that a Rekor transparency log entry is consistent with a published
|
|
15
15
|
* package.
|
|
@@ -43,9 +43,11 @@ export async function verifyRekorEntry(entryId, packageHash, options = {}) {
|
|
|
43
43
|
const verified = recordedAlgo === 'sha512' && recordedHash === expectedHash;
|
|
44
44
|
// Deepen #8 (civic-ai-tools-website#119): when the entry carries an inclusion
|
|
45
45
|
// proof, cryptographically verify Merkle inclusion against the pinned log key —
|
|
46
|
-
// the same offline check a carried bundle runs, here over the fetched entry
|
|
46
|
+
// the same offline check a carried bundle runs, here over the fetched entry, and
|
|
47
|
+
// bound to this record: an entry that does not record `packageHash` is not
|
|
48
|
+
// verified (`rekor-binding.ts`).
|
|
47
49
|
const proof = entry.verification?.inclusionProof;
|
|
48
|
-
const inclusion = proof && entry.body ?
|
|
50
|
+
const inclusion = proof && entry.body ? verifyRekorInclusionForRecord(entry.body, proof, packageHash) : undefined;
|
|
49
51
|
return {
|
|
50
52
|
verified,
|
|
51
53
|
logIndex: entry.logIndex,
|
package/dist/verify.d.ts
CHANGED
|
@@ -140,7 +140,8 @@ export interface VerifyResult {
|
|
|
140
140
|
} | null;
|
|
141
141
|
rekorIntegratedTime?: number;
|
|
142
142
|
/** Cryptographic Merkle-inclusion verdict (#119): null when no proof was
|
|
143
|
-
* available, else the graded result (`inclusionVerified` / `checkpointVerified`)
|
|
143
|
+
* available, else the graded result (`inclusionVerified` / `checkpointVerified`),
|
|
144
|
+
* both false when the log entry does not record `packageHash`.
|
|
144
145
|
* Additive to `rekorVerified` (hash-parity). */
|
|
145
146
|
rekorInclusion: RekorInclusionResult | null;
|
|
146
147
|
hasRekor: boolean;
|
package/dist/verify.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
import { recomputePackageHash } from "./checks.js";
|
|
32
32
|
import { verifySignature } from "./signature.js";
|
|
33
33
|
import { verifyRekorEntry } from "./rekor.js";
|
|
34
|
-
import {
|
|
34
|
+
import { verifyRekorInclusionForRecord } from "./rekor-binding.js";
|
|
35
35
|
import { verifyRfc3161Timestamp } from "./rfc3161.js";
|
|
36
36
|
import { verifyKeyTrust, legacyEmbeddedKeyTrust, } from "./trust-registry.js";
|
|
37
37
|
import { applyKeyDerivedRegistryRule, applySelfCertifiedKeyTrust, compareKeyDerivedIdentifier, } from "./self-certifying.js";
|
|
@@ -128,6 +128,7 @@ export async function verifyRecord(input, deps) {
|
|
|
128
128
|
// Merkle inclusion (#119) is verified cryptographically. The carried proof + body
|
|
129
129
|
// verify inclusion OFFLINE (no fetch); otherwise the online fetch's entry carries
|
|
130
130
|
// the proof. Carried-offline takes precedence — it is the zero-dependency property.
|
|
131
|
+
// Either way the entry must record this record's hash (`rekor-binding.ts`).
|
|
131
132
|
let rekorVerified = null;
|
|
132
133
|
let rekorDetails = null;
|
|
133
134
|
let rekorIntegratedTime;
|
|
@@ -146,7 +147,7 @@ export async function verifyRecord(input, deps) {
|
|
|
146
147
|
rekorInclusion = rekorResult.inclusion;
|
|
147
148
|
}
|
|
148
149
|
if (input.rekorInclusionProof && input.rekorEntryBody) {
|
|
149
|
-
rekorInclusion =
|
|
150
|
+
rekorInclusion = verifyRekorInclusionForRecord(input.rekorEntryBody, input.rekorInclusionProof, packageHash);
|
|
150
151
|
}
|
|
151
152
|
// Step 3c — RFC 3161 timestamp (check #7, deep). Presence is `hasTimestamp`; when a
|
|
152
153
|
// token + package hash are present, cryptographically verify the TSA signature over
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typedstandards/verify-core",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Portable, browser-safe verification core for Typed Standards record packages — the §9.2 check suite, single-sourced so it verifies identically on a server and in the browser.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nathan Storey",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22",
|
|
40
|
-
"typescript": "^
|
|
40
|
+
"typescript": "^6.0.3"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|