@entpoind/verify 0.0.0-stage → 0.2.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +211 -2
- package/dist/canonical-json.d.ts +19 -0
- package/dist/canonical-json.js +58 -0
- package/dist/ed25519ph.d.ts +25 -0
- package/dist/ed25519ph.js +171 -0
- package/dist/index.d.ts +222 -0
- package/dist/index.js +350 -0
- package/dist/merkle.d.ts +24 -0
- package/dist/merkle.js +76 -0
- package/dist/witness.d.ts +175 -0
- package/dist/witness.js +523 -0
- package/package.json +30 -4
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @entpoind/verify — the receipt ledger's public witness: a batch root's entry in the Sigstore Rekor log.
|
|
3
|
+
*
|
|
4
|
+
* What the ledger logs (runtime/shared/rekor-witness.ts, by the hourly cron job):
|
|
5
|
+
*
|
|
6
|
+
* rootMessage the canonical JSON the batch's `rootSignature` is over (ledgerRootMessage)
|
|
7
|
+
* A SHA-512(UTF-8 rootMessage) `witness.artifactSha512`
|
|
8
|
+
* data.hash SHA-512(A) `witness.rekorSha512`, the hash in the `hashedrekord` entry
|
|
9
|
+
* signature Ed25519ph by the region's receipt key, the SAME key (`kid`) as the root's, with A as the message,
|
|
10
|
+
* so its prehash is SHA-512(A) = data.hash
|
|
11
|
+
*
|
|
12
|
+
* Offline (always): the witness's hashes are recomputed from the root message, the witness is by the root's key, its
|
|
13
|
+
* Ed25519ph signature verifies over the recomputed hash with that key, and the copy of the log's answer the proof carries
|
|
14
|
+
* (`body`, `entryUuid`, `inclusionProof`) is that entry and hangs together. That copy came from Entpoind, so it shows the
|
|
15
|
+
* witness is consistent, not that the log holds it.
|
|
16
|
+
*
|
|
17
|
+
* Online (opt-in): the entry is fetched from the log itself and checked again, with the log's own inclusion proof,
|
|
18
|
+
* signed checkpoint and signed entry timestamp verified against the log's public key (ECDSA P-256, as rekor.sigstore.dev
|
|
19
|
+
* uses). The key is pinned by the caller, or fetched from the log, which is then trust on first use; the result says which.
|
|
20
|
+
*
|
|
21
|
+
* Nothing here can fail the receipt or the ledger proof: a witness that is absent, off, pending, inconsistent or
|
|
22
|
+
* unreachable is reported as exactly that, next to a ledger proof that verified on its own. `off` means the receipt's
|
|
23
|
+
* workspace had turned the public witness off before the receipt was batched: the batch's root was never submitted, by
|
|
24
|
+
* the workspace's choice. It is not a failure.
|
|
25
|
+
*/
|
|
26
|
+
export declare const WITNESS_KIND = "rekor.hashedrekord.v0.0.1";
|
|
27
|
+
export declare const DEFAULT_REKOR_URL = "https://rekor.sigstore.dev";
|
|
28
|
+
export declare const WITNESS_LOG_TIMEOUT_MS = 10000;
|
|
29
|
+
/** `witness` in the ledger proof, as the Tools Worker serves it. */
|
|
30
|
+
export type LedgerWitness =
|
|
31
|
+
/** The workspace had turned the public witness off when the batch was built; its root is never submitted. */
|
|
32
|
+
{
|
|
33
|
+
status: "off";
|
|
34
|
+
} | {
|
|
35
|
+
status: "pending";
|
|
36
|
+
since: string;
|
|
37
|
+
attempts: number;
|
|
38
|
+
lastError: string | null;
|
|
39
|
+
nextAttemptAt: string | null;
|
|
40
|
+
} | {
|
|
41
|
+
status: "logged";
|
|
42
|
+
kind: string;
|
|
43
|
+
log: string;
|
|
44
|
+
logIndex: number;
|
|
45
|
+
entryUuid: string;
|
|
46
|
+
integratedTime: number;
|
|
47
|
+
integratedAt: string;
|
|
48
|
+
searchUrl: string;
|
|
49
|
+
entryUrl: string;
|
|
50
|
+
artifactSha512: string;
|
|
51
|
+
rekorSha512: string;
|
|
52
|
+
signature: string;
|
|
53
|
+
signatureKeyId: string;
|
|
54
|
+
logId: string;
|
|
55
|
+
body: string;
|
|
56
|
+
inclusionProof: {
|
|
57
|
+
logIndex: number;
|
|
58
|
+
rootHash: string;
|
|
59
|
+
treeSize: number;
|
|
60
|
+
hashes: string[];
|
|
61
|
+
checkpoint: string;
|
|
62
|
+
} | null;
|
|
63
|
+
signedEntryTimestamp: string;
|
|
64
|
+
};
|
|
65
|
+
/** Why a logged witness is not consistent with the ledger proof. Each is exact: several can be reported at once. */
|
|
66
|
+
export type WitnessProblem =
|
|
67
|
+
/** A field the check needs is missing or of the wrong type. */
|
|
68
|
+
"malformed_witness" | "unknown_witness_kind"
|
|
69
|
+
/** `artifactSha512` is not SHA-512 of the root message. */
|
|
70
|
+
| "artifact_hash_mismatch"
|
|
71
|
+
/** `rekorSha512` is not SHA-512 of the artifact: not the hash the root message gives. */
|
|
72
|
+
| "rekor_hash_mismatch"
|
|
73
|
+
/** `signatureKeyId` is not the key that signed the root. */
|
|
74
|
+
| "not_the_root_key"
|
|
75
|
+
/** The Ed25519ph signature does not verify over the recomputed hash with the root's key. */
|
|
76
|
+
| "bad_witness_signature"
|
|
77
|
+
/** The log entry (`body`) is not a decodable `hashedrekord`. */
|
|
78
|
+
| "entry_body_malformed"
|
|
79
|
+
/** The entry's `data.hash` is not `{ sha512, <the recomputed hash> }`. */
|
|
80
|
+
| "entry_hash_mismatch"
|
|
81
|
+
/** The entry's public key is not the root's key. */
|
|
82
|
+
| "entry_key_mismatch"
|
|
83
|
+
/** The entry's signature is not the witness signature. */
|
|
84
|
+
| "entry_signature_mismatch"
|
|
85
|
+
/** `entryUuid` does not end in the RFC 6962 leaf hash of the entry. */
|
|
86
|
+
| "entry_uuid_mismatch"
|
|
87
|
+
/** The copied inclusion proof does not fold the entry to the root hash it names. */
|
|
88
|
+
| "inclusion_proof_invalid"
|
|
89
|
+
/** `integratedAt` is not the instant `integratedTime` names. */
|
|
90
|
+
| "integrated_at_mismatch";
|
|
91
|
+
/** What the log itself said, when it was asked (`witnessLog`). */
|
|
92
|
+
export type WitnessLogProblem =
|
|
93
|
+
/** The log has no entry with that UUID (404). */
|
|
94
|
+
"entry_not_in_log" | "bad_log_response" | "entry_uuid_mismatch" | "entry_body_malformed" | "entry_hash_mismatch" | "entry_key_mismatch" | "bad_witness_signature"
|
|
95
|
+
/** The log's `logIndex` / `integratedTime` are not the ones the proof states. */
|
|
96
|
+
| "log_index_mismatch" | "integrated_time_mismatch"
|
|
97
|
+
/** The log's public key could not be fetched, is not ECDSA P-256, or does not hash to the entry's `logID`. */
|
|
98
|
+
| "log_key_unavailable" | "log_key_unsupported" | "log_key_not_log_id" | "bad_signed_entry_timestamp" | "no_inclusion_proof" | "inclusion_proof_invalid" | "checkpoint_malformed"
|
|
99
|
+
/** The checkpoint names another tree size or root hash than the inclusion proof. */
|
|
100
|
+
| "checkpoint_mismatch" | "bad_checkpoint_signature";
|
|
101
|
+
export type WitnessPart = "verified" | "failed" | "not_checked";
|
|
102
|
+
export type WitnessLogCheck =
|
|
103
|
+
/** The log was not asked, or did not answer: nothing about the witness was judged. */
|
|
104
|
+
{
|
|
105
|
+
checked: false;
|
|
106
|
+
logUrl: string;
|
|
107
|
+
reason: "log_url_not_https" | "no_entry_uuid" | "log_unreachable" | "log_error";
|
|
108
|
+
status?: number;
|
|
109
|
+
} | {
|
|
110
|
+
checked: true;
|
|
111
|
+
logUrl: string;
|
|
112
|
+
/** True only when every part below is `verified` and there is no problem. */
|
|
113
|
+
ok: boolean;
|
|
114
|
+
problems: WitnessLogProblem[];
|
|
115
|
+
/** `pinned`: the caller's `logPublicKey`. `fetched`: from the log's own /api/v1/log/publicKey, trust on first use. */
|
|
116
|
+
logKey: "pinned" | "fetched" | "unavailable";
|
|
117
|
+
/** The log's time for the entry (Unix seconds), as the log answered. */
|
|
118
|
+
integratedTime: number | null;
|
|
119
|
+
inclusionProof: WitnessPart;
|
|
120
|
+
checkpoint: WitnessPart;
|
|
121
|
+
signedEntryTimestamp: WitnessPart;
|
|
122
|
+
};
|
|
123
|
+
export type WitnessReport =
|
|
124
|
+
/** The proof carries no witness: the batch has not been submitted yet, or the witness is not switched on. */
|
|
125
|
+
{
|
|
126
|
+
status: "absent";
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Not witnessed, by the workspace's choice: its owner or admin had turned the public witness off before this receipt was
|
|
130
|
+
* batched, so the batch's root was never submitted to the log. Not a failure, and nothing to retry.
|
|
131
|
+
*/
|
|
132
|
+
| {
|
|
133
|
+
status: "off";
|
|
134
|
+
}
|
|
135
|
+
/** Tried, not in the log yet. The batch itself is complete. */
|
|
136
|
+
| {
|
|
137
|
+
status: "pending";
|
|
138
|
+
since: string | null;
|
|
139
|
+
attempts: number | null;
|
|
140
|
+
lastError: string | null;
|
|
141
|
+
nextAttemptAt: string | null;
|
|
142
|
+
}
|
|
143
|
+
/** A `witness` whose status is none of the three the API defines (`off`, `pending`, `logged`). */
|
|
144
|
+
| {
|
|
145
|
+
status: "malformed";
|
|
146
|
+
} | {
|
|
147
|
+
status: "logged";
|
|
148
|
+
/** The witness is the root's: its hashes, key and signature, and the copy of the log's answer, all check out. */
|
|
149
|
+
consistent: boolean;
|
|
150
|
+
problems: WitnessProblem[];
|
|
151
|
+
log: string | null;
|
|
152
|
+
logIndex: number | null;
|
|
153
|
+
entryUuid: string | null;
|
|
154
|
+
integratedAt: string | null;
|
|
155
|
+
searchUrl: string | null;
|
|
156
|
+
/** Present only when the log was asked (`witnessLog`). */
|
|
157
|
+
logCheck?: WitnessLogCheck;
|
|
158
|
+
};
|
|
159
|
+
export type WitnessLogOptions = {
|
|
160
|
+
/** Overrides the proof's `witness.log`. Must be https. Default: the proof's, else https://rekor.sigstore.dev. */
|
|
161
|
+
logUrl?: string;
|
|
162
|
+
/** The log's public key (SubjectPublicKeyInfo PEM) to verify its signatures with. Without it, the key is fetched from the log. */
|
|
163
|
+
logPublicKey?: string;
|
|
164
|
+
fetch?: typeof fetch;
|
|
165
|
+
timeoutMs?: number;
|
|
166
|
+
};
|
|
167
|
+
export type WitnessContext = {
|
|
168
|
+
/** The message the root signature is over, rebuilt from the proof. */
|
|
169
|
+
rootMessage: string;
|
|
170
|
+
/** The root's key id, and its key from the published set (already used to verify the root). */
|
|
171
|
+
signatureKeyId: string;
|
|
172
|
+
publicKeyX: string;
|
|
173
|
+
};
|
|
174
|
+
/** Never throws: anything the witness holds that cannot be checked is a problem, reported. */
|
|
175
|
+
export declare function checkLedgerWitness(witness: unknown, ctx: WitnessContext, log?: WitnessLogOptions): Promise<WitnessReport>;
|