@appforge-ci/core 0.3.0 → 0.3.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/dist/agent-readiness.d.ts +11 -3
- package/dist/agent-readiness.d.ts.map +1 -1
- package/dist/agent-readiness.js +15 -5
- package/dist/agent-readiness.js.map +1 -1
- package/dist/agent-readiness.test.js +34 -10
- package/dist/agent-readiness.test.js.map +1 -1
- package/dist/api-client.d.ts +39 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +52 -0
- package/dist/api-client.js.map +1 -1
- package/dist/appforge-verify.test.d.ts +2 -0
- package/dist/appforge-verify.test.d.ts.map +1 -0
- package/dist/appforge-verify.test.js +958 -0
- package/dist/appforge-verify.test.js.map +1 -0
- package/dist/attestation-keys.d.ts +20 -0
- package/dist/attestation-keys.d.ts.map +1 -0
- package/dist/attestation-keys.js +27 -0
- package/dist/attestation-keys.js.map +1 -0
- package/dist/attestation-keys.test.d.ts +2 -0
- package/dist/attestation-keys.test.d.ts.map +1 -0
- package/dist/attestation-keys.test.js +45 -0
- package/dist/attestation-keys.test.js.map +1 -0
- package/dist/attestation-signing.d.ts +226 -0
- package/dist/attestation-signing.d.ts.map +1 -0
- package/dist/attestation-signing.js +276 -0
- package/dist/attestation-signing.js.map +1 -0
- package/dist/attestation-signing.test.d.ts +2 -0
- package/dist/attestation-signing.test.d.ts.map +1 -0
- package/dist/attestation-signing.test.js +250 -0
- package/dist/attestation-signing.test.js.map +1 -0
- package/dist/attestation-workerd.test.d.ts +2 -0
- package/dist/attestation-workerd.test.d.ts.map +1 -0
- package/dist/attestation-workerd.test.js +183 -0
- package/dist/attestation-workerd.test.js.map +1 -0
- package/dist/attestation.d.ts +169 -22
- package/dist/attestation.d.ts.map +1 -1
- package/dist/attestation.js +42 -16
- package/dist/attestation.js.map +1 -1
- package/dist/build-vpn.d.ts +11 -1
- package/dist/build-vpn.d.ts.map +1 -1
- package/dist/build-vpn.js +27 -0
- package/dist/build-vpn.js.map +1 -1
- package/dist/fleet-vpn.d.ts +64 -0
- package/dist/fleet-vpn.d.ts.map +1 -0
- package/dist/fleet-vpn.js +88 -0
- package/dist/fleet-vpn.js.map +1 -0
- package/dist/fleet-vpn.test.d.ts +2 -0
- package/dist/fleet-vpn.test.d.ts.map +1 -0
- package/dist/fleet-vpn.test.js +69 -0
- package/dist/fleet-vpn.test.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/schemas-repo-url.test.d.ts +2 -0
- package/dist/schemas-repo-url.test.d.ts.map +1 -0
- package/dist/schemas-repo-url.test.js +67 -0
- package/dist/schemas-repo-url.test.js.map +1 -0
- package/dist/schemas.d.ts +18 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +56 -10
- package/dist/schemas.js.map +1 -1
- package/dist/ssh-deploy-key.d.ts +59 -0
- package/dist/ssh-deploy-key.d.ts.map +1 -0
- package/dist/ssh-deploy-key.js +124 -0
- package/dist/ssh-deploy-key.js.map +1 -0
- package/dist/ssh-deploy-key.test.d.ts +2 -0
- package/dist/ssh-deploy-key.test.d.ts.map +1 -0
- package/dist/ssh-deploy-key.test.js +117 -0
- package/dist/ssh-deploy-key.test.js.map +1 -0
- package/dist/types.d.ts +57 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +1 -1
- package/dist/vpn-attestation-workerd.test.d.ts +2 -0
- package/dist/vpn-attestation-workerd.test.d.ts.map +1 -0
- package/dist/vpn-attestation-workerd.test.js +184 -0
- package/dist/vpn-attestation-workerd.test.js.map +1 -0
- package/dist/vpn-attestation.d.ts +1015 -0
- package/dist/vpn-attestation.d.ts.map +1 -0
- package/dist/vpn-attestation.js +963 -0
- package/dist/vpn-attestation.js.map +1 -0
- package/dist/vpn-attestation.test.d.ts +2 -0
- package/dist/vpn-attestation.test.d.ts.map +1 -0
- package/dist/vpn-attestation.test.js +613 -0
- package/dist/vpn-attestation.test.js.map +1 -0
- package/dist/vpn-egress-presets.d.ts +1 -1
- package/dist/vpn-egress-presets.d.ts.map +1 -1
- package/dist/vpn-egress-presets.js +2 -2
- package/dist/vpn-egress-presets.js.map +1 -1
- package/dist/vpn-sandbox.d.ts +67 -0
- package/dist/vpn-sandbox.d.ts.map +1 -0
- package/dist/vpn-sandbox.js +22 -0
- package/dist/vpn-sandbox.js.map +1 -0
- package/dist/vpn-sandbox.test.d.ts +2 -0
- package/dist/vpn-sandbox.test.d.ts.map +1 -0
- package/dist/vpn-sandbox.test.js +34 -0
- package/dist/vpn-sandbox.test.js.map +1 -0
- package/dist/vpn-ssh.d.ts +93 -0
- package/dist/vpn-ssh.d.ts.map +1 -1
- package/dist/vpn-ssh.js +213 -1
- package/dist/vpn-ssh.js.map +1 -1
- package/dist/vpn-ssh.test.js +200 -1
- package/dist/vpn-ssh.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { base64ToBytes, bytesToBase64, canonicalJson, createEd25519Signer, ed25519KeyId, verifyEd25519, auditCheckpointSigningBytes, auditCheckpointStatement, } from "./attestation.js";
|
|
3
|
+
/**
|
|
4
|
+
* Signing and key handling for the regulated-VPN evidence (PR-K).
|
|
5
|
+
*
|
|
6
|
+
* One Ed25519 key signs for every org. The private half is the Worker secret
|
|
7
|
+
* `ATTESTATION_SIGNING_KEY` on apps/admin-api (where the sealer runs); this
|
|
8
|
+
* module never sees it anywhere else. The public halves are published, as a
|
|
9
|
+
* list of key entries, at `GET /.well-known/appforge-attestation-keys` on
|
|
10
|
+
* apps/api and in `docs/attestation-keys.json`, both generated from
|
|
11
|
+
* `ATTESTATION_PUBLIC_KEYS` (attestation-keys.ts). Key ids are
|
|
12
|
+
* self-certifying (`ed25519KeyId`): the first 16 hex characters of the
|
|
13
|
+
* sha256 of the raw public key, recomputed by every consumer, so a key list
|
|
14
|
+
* cannot mislabel a key.
|
|
15
|
+
*
|
|
16
|
+
* Rotation adds an entry; the old key stays listed as `retired` (with the
|
|
17
|
+
* `notAfter` it stopped signing) so every older signature still verifies.
|
|
18
|
+
* Revocation sets `status: "revoked"` and `revokedAt`: signatures issued at
|
|
19
|
+
* or after that instant stop verifying. That only helps against a thief who
|
|
20
|
+
* cannot backdate: the real defence against a stolen key is copies the
|
|
21
|
+
* customer already holds (`appforge-verify --pin-head`). docs/attestation.md
|
|
22
|
+
* says so.
|
|
23
|
+
*
|
|
24
|
+
* Runtime-neutral: WebCrypto only (Workers, Node 20+).
|
|
25
|
+
*/
|
|
26
|
+
/** Prefix of every attestation-document signature input. The checkpoint domain lives in attestation.ts. */
|
|
27
|
+
export const ATTESTATION_DOMAIN = "appforge-attestation-v1\n";
|
|
28
|
+
const utf8 = new TextEncoder();
|
|
29
|
+
const isoUtc = z
|
|
30
|
+
.string()
|
|
31
|
+
.regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/, "expected an ISO-8601 UTC timestamp without fractions")
|
|
32
|
+
// The shape alone admits "2026-13-01T00:00:00Z", which Date.parse turns into NaN; every window comparison against NaN is false, so a revocation or expiry would silently never apply.
|
|
33
|
+
.refine((v) => Number.isFinite(Date.parse(v)) && new Date(v).toISOString().slice(0, 19) + "Z" === v, "not a real calendar date and time");
|
|
34
|
+
// ---------------------------------------------------------------- key list
|
|
35
|
+
/** One published public key. `kid` is what stored signatures call `keyId`. */
|
|
36
|
+
export const attestationKeyEntrySchema = z
|
|
37
|
+
.object({
|
|
38
|
+
kid: z.string().regex(/^[0-9a-f]{16}$/, "kid must be 16 lowercase hex characters"),
|
|
39
|
+
alg: z.literal("Ed25519"),
|
|
40
|
+
/** base64 of the raw 32-byte public key. */
|
|
41
|
+
publicKey: z.string().regex(/^[A-Za-z0-9+/]{43}=$/, "publicKey must be base64 of 32 bytes"),
|
|
42
|
+
/** Signatures issued before this instant do not verify under this key. */
|
|
43
|
+
notBefore: isoUtc,
|
|
44
|
+
/** Signatures issued at or after this instant do not verify. Required once `status` is `retired`. */
|
|
45
|
+
notAfter: isoUtc.optional(),
|
|
46
|
+
status: z.enum(["active", "retired", "revoked"]),
|
|
47
|
+
/** Required exactly when `status` is `revoked`: signatures issued at or after it do not verify. */
|
|
48
|
+
revokedAt: isoUtc.optional(),
|
|
49
|
+
})
|
|
50
|
+
.strict()
|
|
51
|
+
.superRefine((k, ctx) => {
|
|
52
|
+
if (k.status === "revoked" && !k.revokedAt)
|
|
53
|
+
ctx.addIssue({ code: "custom", path: ["revokedAt"], message: "a revoked key needs revokedAt" });
|
|
54
|
+
if (k.status !== "revoked" && k.revokedAt)
|
|
55
|
+
ctx.addIssue({ code: "custom", path: ["revokedAt"], message: "revokedAt is only valid on a revoked key" });
|
|
56
|
+
if (k.status === "retired" && !k.notAfter)
|
|
57
|
+
ctx.addIssue({ code: "custom", path: ["notAfter"], message: "a retired key needs notAfter" });
|
|
58
|
+
if (k.notAfter && Date.parse(k.notAfter) <= Date.parse(k.notBefore))
|
|
59
|
+
ctx.addIssue({ code: "custom", path: ["notAfter"], message: "notAfter must be after notBefore" });
|
|
60
|
+
});
|
|
61
|
+
/** What `GET /.well-known/appforge-attestation-keys` returns. */
|
|
62
|
+
export const attestationKeyListSchema = z.array(attestationKeyEntrySchema);
|
|
63
|
+
/**
|
|
64
|
+
* Validates a key list and indexes it by `kid`. Rejects the whole list (never
|
|
65
|
+
* silently drops an entry) when an entry is malformed, a `kid` does not match
|
|
66
|
+
* its public key, or a `kid` repeats.
|
|
67
|
+
*/
|
|
68
|
+
export async function buildAttestationKeySet(entries) {
|
|
69
|
+
const parsed = attestationKeyListSchema.safeParse(entries);
|
|
70
|
+
if (!parsed.success) {
|
|
71
|
+
return { ok: false, problems: parsed.error.issues.map((i) => `key list ${i.path.join(".") || "(root)"}: ${i.message}`) };
|
|
72
|
+
}
|
|
73
|
+
const problems = [];
|
|
74
|
+
const keys = new Map();
|
|
75
|
+
for (const entry of parsed.data) {
|
|
76
|
+
let raw;
|
|
77
|
+
try {
|
|
78
|
+
raw = base64ToBytes(entry.publicKey);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
problems.push(`key ${entry.kid}: publicKey is not valid base64`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (raw.length !== 32) {
|
|
85
|
+
problems.push(`key ${entry.kid}: publicKey is not 32 bytes`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if ((await ed25519KeyId(raw)) !== entry.kid) {
|
|
89
|
+
problems.push(`key ${entry.kid}: kid does not match its public key`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (keys.has(entry.kid)) {
|
|
93
|
+
problems.push(`key ${entry.kid}: listed twice`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
keys.set(entry.kid, { entry, publicKey: raw });
|
|
97
|
+
}
|
|
98
|
+
return problems.length ? { ok: false, problems } : { ok: true, keys };
|
|
99
|
+
}
|
|
100
|
+
/** Why a key may not vouch for something issued at `issuedAt`, or null if it may. */
|
|
101
|
+
export function attestationKeyProblemAt(entry, issuedAt) {
|
|
102
|
+
const t = Date.parse(issuedAt);
|
|
103
|
+
if (!Number.isFinite(t))
|
|
104
|
+
return "issue time is not a valid timestamp";
|
|
105
|
+
if (t < Date.parse(entry.notBefore))
|
|
106
|
+
return "key was not yet valid at the issue time";
|
|
107
|
+
if (entry.notAfter && t >= Date.parse(entry.notAfter))
|
|
108
|
+
return "key was retired or expired at the issue time";
|
|
109
|
+
if (entry.status === "revoked" && entry.revokedAt && t >= Date.parse(entry.revokedAt))
|
|
110
|
+
return "key was revoked as of the issue time";
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/** The exact bytes that are signed: `domain + canonicalJson(value)`. */
|
|
114
|
+
export function domainSigningBytes(domain, value) {
|
|
115
|
+
return utf8.encode(domain + canonicalJson(value));
|
|
116
|
+
}
|
|
117
|
+
/** Signs `domain + canonicalJson(value)`; returns the base64 signature. */
|
|
118
|
+
export async function signWithDomain(signer, domain, value) {
|
|
119
|
+
return bytesToBase64(await signer.sign(domainSigningBytes(domain, value)));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Verifies a stored signature against a published key set: the key must be
|
|
123
|
+
* listed, valid at `issuedAt`, and the Ed25519 signature must cover exactly
|
|
124
|
+
* `domain + canonicalJson(value)`. Never throws.
|
|
125
|
+
*/
|
|
126
|
+
export async function verifyWithDomain(keys, input) {
|
|
127
|
+
const key = keys.get(input.keyId);
|
|
128
|
+
if (!key)
|
|
129
|
+
return { ok: false, reason: `signing key ${input.keyId} is not in the trusted key set` };
|
|
130
|
+
const problem = attestationKeyProblemAt(key.entry, input.issuedAt);
|
|
131
|
+
if (problem)
|
|
132
|
+
return { ok: false, reason: problem };
|
|
133
|
+
try {
|
|
134
|
+
const ok = await verifyEd25519(key.publicKey, domainSigningBytes(input.domain, input.value), base64ToBytes(input.signature));
|
|
135
|
+
return ok ? { ok: true, keyId: input.keyId } : { ok: false, reason: "signature does not verify" };
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { ok: false, reason: "signature does not verify" };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** A signature over an attestation document (PR-M assembles the document; `role` leaves room for a later agent co-signature). */
|
|
142
|
+
export const attestationSignatureSchema = z
|
|
143
|
+
.object({ alg: z.literal("Ed25519"), keyId: z.string().min(1).max(64), role: z.enum(["server", "agent"]), signature: z.string().min(1).max(256) })
|
|
144
|
+
.strict();
|
|
145
|
+
/** Signs an attestation document with the server key. */
|
|
146
|
+
export async function signAttestationDocument(signer, doc) {
|
|
147
|
+
isoUtc.parse(doc.issuedAt);
|
|
148
|
+
const keyProblem = signer.problemAt?.(doc.issuedAt);
|
|
149
|
+
if (keyProblem)
|
|
150
|
+
throw new Error(`signing key ${signer.keyId} cannot sign a document issued at ${doc.issuedAt}: ${keyProblem}`);
|
|
151
|
+
return { alg: "Ed25519", keyId: signer.keyId, role: "server", signature: await signWithDomain(signer, ATTESTATION_DOMAIN, doc) };
|
|
152
|
+
}
|
|
153
|
+
export function verifyAttestationSignature(keys, doc, sig) {
|
|
154
|
+
return verifyWithDomain(keys, { domain: ATTESTATION_DOMAIN, value: doc, keyId: sig.keyId, signature: sig.signature, issuedAt: doc.issuedAt });
|
|
155
|
+
}
|
|
156
|
+
/** Verifies a stored checkpoint (signature, key, and the key's validity window at `issuedAt`) against a key set. */
|
|
157
|
+
export async function verifyAuditCheckpoint(keys, checkpoint) {
|
|
158
|
+
let bytes;
|
|
159
|
+
try {
|
|
160
|
+
bytes = auditCheckpointSigningBytes(auditCheckpointStatement({ orgId: checkpoint.orgId, seq: checkpoint.seq, hash: checkpoint.hash, issuedAt: checkpoint.issuedAt }));
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return { ok: false, reason: "checkpoint is malformed" };
|
|
164
|
+
}
|
|
165
|
+
const key = keys.get(checkpoint.keyId);
|
|
166
|
+
if (!key)
|
|
167
|
+
return { ok: false, reason: `signing key ${checkpoint.keyId} is not in the trusted key set` };
|
|
168
|
+
const problem = attestationKeyProblemAt(key.entry, checkpoint.issuedAt);
|
|
169
|
+
if (problem)
|
|
170
|
+
return { ok: false, reason: problem };
|
|
171
|
+
try {
|
|
172
|
+
return (await verifyEd25519(key.publicKey, bytes, base64ToBytes(checkpoint.signature)))
|
|
173
|
+
? { ok: true, keyId: checkpoint.keyId }
|
|
174
|
+
: { ok: false, reason: "signature does not verify" };
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return { ok: false, reason: "signature does not verify" };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const PKCS8_ED25519_PREFIX_HEX = "302e020100300506032b657004220420";
|
|
181
|
+
function base64UrlToBytes(s) {
|
|
182
|
+
const padded = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
183
|
+
return base64ToBytes(padded + "=".repeat((4 - (padded.length % 4)) % 4));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Accepts what `wrangler secret put` is fed: base64 of the PKCS#8 DER private
|
|
187
|
+
* key, optionally wrapped as a PEM (armor and whitespace are ignored).
|
|
188
|
+
*/
|
|
189
|
+
function decodePkcs8Secret(secret) {
|
|
190
|
+
const body = secret
|
|
191
|
+
.replace(/-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----/g, "")
|
|
192
|
+
.replace(/\s+/g, "");
|
|
193
|
+
let bytes;
|
|
194
|
+
try {
|
|
195
|
+
bytes = base64ToBytes(body);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const prefix = PKCS8_ED25519_PREFIX_HEX.match(/../g).map((h) => parseInt(h, 16));
|
|
201
|
+
if (bytes.length !== 48 || prefix.some((b, i) => bytes[i] !== b))
|
|
202
|
+
return null;
|
|
203
|
+
return bytes;
|
|
204
|
+
}
|
|
205
|
+
/** Derives the raw public key of a PKCS#8 Ed25519 private key (WebCrypto has no direct call: export the JWK of an extractable import). */
|
|
206
|
+
async function publicKeyOfPkcs8(pkcs8) {
|
|
207
|
+
const key = await crypto.subtle.importKey("pkcs8", pkcs8, { name: "Ed25519" }, true, ["sign"]);
|
|
208
|
+
const jwk = (await crypto.subtle.exportKey("jwk", key));
|
|
209
|
+
if (!jwk.x)
|
|
210
|
+
throw new Error("no public component");
|
|
211
|
+
const raw = base64UrlToBytes(jwk.x);
|
|
212
|
+
if (raw.length !== 32)
|
|
213
|
+
throw new Error("bad public component");
|
|
214
|
+
return raw;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Builds the signer from the secret, refusing unless the key is one the
|
|
218
|
+
* published list vouches for right now:
|
|
219
|
+
*
|
|
220
|
+
* - no secret -> `not_configured` (checkpoints stay unsigned, visibly)
|
|
221
|
+
* - not a PKCS#8 Ed25519 key -> `invalid`
|
|
222
|
+
* - key id absent from the list, or listed but not `active` / not valid at `now`
|
|
223
|
+
* -> `unpublished` (nobody could verify what it signs)
|
|
224
|
+
* - otherwise a sign + verify round trip with the published key must pass -> `ready`
|
|
225
|
+
*
|
|
226
|
+
* `kid` is derived from the secret itself, so it cannot be misconfigured.
|
|
227
|
+
* Reasons never contain secret material.
|
|
228
|
+
*/
|
|
229
|
+
export async function loadAttestationSigner(secret, published, now = new Date()) {
|
|
230
|
+
if (secret === undefined || secret === null || secret.trim() === "")
|
|
231
|
+
return { state: "not_configured" };
|
|
232
|
+
const pkcs8 = decodePkcs8Secret(secret);
|
|
233
|
+
if (!pkcs8)
|
|
234
|
+
return { state: "invalid", reason: "ATTESTATION_SIGNING_KEY is not a base64 PKCS#8 Ed25519 private key" };
|
|
235
|
+
let rawPublic;
|
|
236
|
+
try {
|
|
237
|
+
rawPublic = await publicKeyOfPkcs8(pkcs8);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return { state: "invalid", reason: "ATTESTATION_SIGNING_KEY could not be imported as an Ed25519 private key" };
|
|
241
|
+
}
|
|
242
|
+
const keyId = await ed25519KeyId(rawPublic);
|
|
243
|
+
const set = await buildAttestationKeySet(published);
|
|
244
|
+
if (!set.ok)
|
|
245
|
+
return { state: "invalid", reason: `the published key list is invalid: ${set.problems.join("; ")}` };
|
|
246
|
+
const listed = set.keys.get(keyId);
|
|
247
|
+
if (!listed)
|
|
248
|
+
return { state: "unpublished", keyId, reason: `signing key ${keyId} is not in the published key list (add its public key before enabling signing)` };
|
|
249
|
+
if (listed.entry.status !== "active")
|
|
250
|
+
return { state: "unpublished", keyId, reason: `signing key ${keyId} is listed as ${listed.entry.status}, not active` };
|
|
251
|
+
const window = attestationKeyProblemAt(listed.entry, now.toISOString().slice(0, 19) + "Z");
|
|
252
|
+
if (window)
|
|
253
|
+
return { state: "unpublished", keyId, reason: `signing key ${keyId}: ${window}` };
|
|
254
|
+
if (bytesEqual(listed.publicKey, rawPublic) === false)
|
|
255
|
+
return { state: "invalid", reason: "the published public key does not match the signing key" };
|
|
256
|
+
const base = await createEd25519Signer(keyId, pkcs8);
|
|
257
|
+
// The window above was checked once, at `now`. Whoever stamps an `issuedAt` later in the tick re-checks it at that instant.
|
|
258
|
+
const entry = listed.entry;
|
|
259
|
+
const signer = { keyId: base.keyId, sign: (m) => base.sign(m), problemAt: (issuedAt) => attestationKeyProblemAt(entry, issuedAt) };
|
|
260
|
+
// Round trip through the published key: proves this runtime can sign and that what it signs verifies.
|
|
261
|
+
const probe = { type: "appforge.signing.self-test", keyId };
|
|
262
|
+
const sig = await signWithDomain(signer, ATTESTATION_DOMAIN, probe);
|
|
263
|
+
const verdict = await verifyWithDomain(set.keys, { domain: ATTESTATION_DOMAIN, value: probe, keyId, signature: sig, issuedAt: now.toISOString().slice(0, 19) + "Z" });
|
|
264
|
+
if (!verdict.ok)
|
|
265
|
+
return { state: "invalid", reason: `signing self-test failed: ${verdict.reason}` };
|
|
266
|
+
return { state: "ready", signer, keyId };
|
|
267
|
+
}
|
|
268
|
+
function bytesEqual(a, b) {
|
|
269
|
+
if (a.length !== b.length)
|
|
270
|
+
return false;
|
|
271
|
+
let diff = 0;
|
|
272
|
+
for (let i = 0; i < a.length; i++)
|
|
273
|
+
diff |= a[i] ^ b[i];
|
|
274
|
+
return diff === 0;
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=attestation-signing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation-signing.js","sourceRoot":"","sources":["../src/attestation-signing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation-signing.test.d.ts","sourceRoot":"","sources":["../src/attestation-signing.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, sign as nodeSign, verify as nodeVerify } from "node:crypto";
|
|
4
|
+
import { auditCheckpointSigningBytes, auditCheckpointStatement, bytesToBase64, createEd25519Signer, ed25519KeyId, ed25519SeedToPkcs8, signAuditCheckpoint, } from "./attestation.js";
|
|
5
|
+
import { ATTESTATION_DOMAIN, attestationKeyProblemAt, buildAttestationKeySet, domainSigningBytes, loadAttestationSigner, signAttestationDocument, signWithDomain, verifyAttestationSignature, verifyAuditCheckpoint, verifyWithDomain, } from "./attestation-signing.js";
|
|
6
|
+
import { ATTESTATION_PUBLIC_KEYS } from "./attestation-keys.js";
|
|
7
|
+
/**
|
|
8
|
+
* Every key in this file is a throwaway TEST key derived at run time from a
|
|
9
|
+
* label (or generated fresh). No production key material exists in the repo.
|
|
10
|
+
* The pinned signatures below were produced by this module AND independently
|
|
11
|
+
* by `node:crypto` (Ed25519 is deterministic, so both must agree byte for byte).
|
|
12
|
+
*/
|
|
13
|
+
const seedFor = (label) => new Uint8Array(createHash("sha256").update(label).digest());
|
|
14
|
+
const pkcs8For = (label) => ed25519SeedToPkcs8(seedFor(label));
|
|
15
|
+
const nodePrivate = (pkcs8) => createPrivateKey({ key: Buffer.from(pkcs8), format: "der", type: "pkcs8" });
|
|
16
|
+
const rawPublicOf = (pkcs8) => new Uint8Array(createPublicKey(nodePrivate(pkcs8)).export({ format: "der", type: "spki" }).subarray(-32));
|
|
17
|
+
async function keyFor(label, over = {}) {
|
|
18
|
+
const pkcs8 = pkcs8For(label);
|
|
19
|
+
const raw = rawPublicOf(pkcs8);
|
|
20
|
+
const kid = await ed25519KeyId(raw);
|
|
21
|
+
const entry = { kid, alg: "Ed25519", publicKey: bytesToBase64(raw), notBefore: "2026-01-01T00:00:00Z", status: "active", ...over };
|
|
22
|
+
return { pkcs8, raw, kid, entry, signer: await createEd25519Signer(kid, pkcs8) };
|
|
23
|
+
}
|
|
24
|
+
const H = "ab".repeat(32);
|
|
25
|
+
const statement = auditCheckpointStatement({ orgId: "org_acme", seq: 7, hash: H, issuedAt: "2026-10-02T14:03:50Z" });
|
|
26
|
+
// ---------------------------------------------------------------- vectors
|
|
27
|
+
test("signature test vectors are stable and agree with an independent implementation", async () => {
|
|
28
|
+
const k = await keyFor("appforge-test-vector-seed-v1");
|
|
29
|
+
assert.equal(k.kid, "fa0e09426c7aabe2", "kid = first 16 hex of sha256(raw public key)");
|
|
30
|
+
assert.equal(k.entry.publicKey, "lKvX3Rlx/fMqt6oFrcMfxoxGbR4GabKtfeb8vb4EoSk=");
|
|
31
|
+
assert.equal(k.kid, createHash("sha256").update(Buffer.from(k.entry.publicKey, "base64")).digest("hex").slice(0, 16), "kid derivation, checked with node:crypto");
|
|
32
|
+
const cp = await signAuditCheckpoint(k.signer, statement);
|
|
33
|
+
assert.equal(cp, "WaJ3iUH1znI8fEAV3o/zvtO6LaP9VIpOTcuuzE0PVqFb/DHJ636nUZXal7I1qME9CJcxGiQMb9S28CsmLHO5DQ==");
|
|
34
|
+
assert.equal(cp, nodeSign(null, Buffer.from(auditCheckpointSigningBytes(statement)), nodePrivate(k.pkcs8)).toString("base64"));
|
|
35
|
+
const doc = { issuedAt: "2026-10-02T14:03:50Z", subject: { buildId: "bld_1", orgId: "org_acme" } };
|
|
36
|
+
const att = await signWithDomain(k.signer, ATTESTATION_DOMAIN, doc);
|
|
37
|
+
assert.equal(att, "5SPJuifSVWrD32lYkGeUQUGi9WZ9IP4axfjWSj0kDmaz3J9UUiBN99/zApL0zCtXr8cxnDRaSLwcAQuphHfvBA==");
|
|
38
|
+
assert.equal(att, nodeSign(null, Buffer.from(`appforge-attestation-v1\n{"issuedAt":"2026-10-02T14:03:50Z","subject":{"buildId":"bld_1","orgId":"org_acme"}}`), nodePrivate(k.pkcs8)).toString("base64"));
|
|
39
|
+
// Determinism: signing twice yields the same bytes, and node:crypto verifies what WebCrypto signed.
|
|
40
|
+
assert.equal(await signWithDomain(k.signer, ATTESTATION_DOMAIN, doc), att);
|
|
41
|
+
assert.equal(nodeVerify(null, Buffer.from(domainSigningBytes(ATTESTATION_DOMAIN, doc)), createPublicKey(nodePrivate(k.pkcs8)), Buffer.from(att, "base64")), true);
|
|
42
|
+
});
|
|
43
|
+
// ---------------------------------------------------------------- key list
|
|
44
|
+
test("buildAttestationKeySet accepts a good list and rejects every malformed or mislabelled entry", async () => {
|
|
45
|
+
const a = await keyFor("k-a");
|
|
46
|
+
const b = await keyFor("k-b", { status: "retired", notAfter: "2026-06-01T00:00:00Z" });
|
|
47
|
+
const good = await buildAttestationKeySet([a.entry, b.entry]);
|
|
48
|
+
assert.equal(good.ok, true);
|
|
49
|
+
if (good.ok)
|
|
50
|
+
assert.deepEqual([...good.keys.keys()].sort(), [a.kid, b.kid].sort());
|
|
51
|
+
assert.equal((await buildAttestationKeySet([])).ok, true, "an empty list is valid (nothing published yet)");
|
|
52
|
+
const bad = [
|
|
53
|
+
["kid not matching the public key", [{ ...a.entry, kid: "0".repeat(16) }], /kid does not match its public key/],
|
|
54
|
+
["duplicate kid", [a.entry, a.entry], /listed twice/],
|
|
55
|
+
["public key of the wrong length", [{ ...a.entry, publicKey: bytesToBase64(new Uint8Array(33)).slice(0, 44) }], /publicKey/],
|
|
56
|
+
["unknown algorithm", [{ ...a.entry, alg: "RSA" }], /alg/],
|
|
57
|
+
["extra field", [{ ...a.entry, comment: "x" }], /Unrecognized key/],
|
|
58
|
+
["revoked without revokedAt", [{ ...a.entry, status: "revoked" }], /revokedAt/],
|
|
59
|
+
["revokedAt on a non-revoked key", [{ ...a.entry, revokedAt: "2026-02-01T00:00:00Z" }], /revokedAt/],
|
|
60
|
+
["retired without notAfter", [{ ...a.entry, status: "retired" }], /notAfter/],
|
|
61
|
+
["notAfter before notBefore", [{ ...a.entry, notAfter: "2025-01-01T00:00:00Z" }], /notAfter must be after/],
|
|
62
|
+
["fractional timestamp", [{ ...a.entry, notBefore: "2026-01-01T00:00:00.5Z" }], /timestamp/],
|
|
63
|
+
["not a list", { keys: [a.entry] }, /expected array/i],
|
|
64
|
+
];
|
|
65
|
+
for (const [name, input, re] of bad) {
|
|
66
|
+
const res = await buildAttestationKeySet(input);
|
|
67
|
+
assert.equal(res.ok, false, name);
|
|
68
|
+
if (!res.ok)
|
|
69
|
+
assert.match(res.problems.join("\n"), re, name);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
test("the published key list in the repo is valid", async () => {
|
|
73
|
+
assert.equal((await buildAttestationKeySet(ATTESTATION_PUBLIC_KEYS)).ok, true);
|
|
74
|
+
});
|
|
75
|
+
test("key validity window: notBefore, notAfter and revokedAt are enforced against the ISSUE time", async () => {
|
|
76
|
+
const k = await keyFor("k-window", { notAfter: "2026-12-01T00:00:00Z" });
|
|
77
|
+
assert.equal(attestationKeyProblemAt(k.entry, "2026-01-01T00:00:00Z"), null, "notBefore is inclusive");
|
|
78
|
+
assert.match(attestationKeyProblemAt(k.entry, "2025-12-31T23:59:59Z"), /not yet valid/);
|
|
79
|
+
assert.equal(attestationKeyProblemAt(k.entry, "2026-11-30T23:59:59Z"), null);
|
|
80
|
+
assert.match(attestationKeyProblemAt(k.entry, "2026-12-01T00:00:00Z"), /retired or expired/, "notAfter is exclusive");
|
|
81
|
+
const revoked = { ...k.entry, status: "revoked", revokedAt: "2026-05-01T00:00:00Z" };
|
|
82
|
+
assert.equal(attestationKeyProblemAt(revoked, "2026-04-30T23:59:59Z"), null, "signatures from before the revocation stay valid");
|
|
83
|
+
assert.match(attestationKeyProblemAt(revoked, "2026-05-01T00:00:00Z"), /revoked/);
|
|
84
|
+
assert.match(attestationKeyProblemAt(k.entry, "not a time"), /valid timestamp/);
|
|
85
|
+
});
|
|
86
|
+
// ---------------------------------------------------------------- rotation, revocation, tamper
|
|
87
|
+
test("rotation: after a new key is added the old key stays listed and its old signatures still verify", async () => {
|
|
88
|
+
const oldKey = await keyFor("rot-old");
|
|
89
|
+
const newKey = await keyFor("rot-new", { notBefore: "2026-07-01T00:00:00Z" });
|
|
90
|
+
const doc = { issuedAt: "2026-03-01T00:00:00Z", n: 1 };
|
|
91
|
+
const oldSig = await signAttestationDocument(oldKey.signer, doc);
|
|
92
|
+
const lateDoc = { issuedAt: "2026-09-01T00:00:00Z", n: 2 };
|
|
93
|
+
const newSig = await signAttestationDocument(newKey.signer, lateDoc);
|
|
94
|
+
// Before rotation: only the old key is listed.
|
|
95
|
+
const before = await buildAttestationKeySet([oldKey.entry]);
|
|
96
|
+
assert.ok(before.ok);
|
|
97
|
+
assert.equal((await verifyAttestationSignature(before.keys, doc, oldSig)).ok, true);
|
|
98
|
+
// After rotation: old key retired at the cut-over, new key active.
|
|
99
|
+
const after = await buildAttestationKeySet([
|
|
100
|
+
{ ...oldKey.entry, status: "retired", notAfter: "2026-07-01T00:00:00Z" },
|
|
101
|
+
newKey.entry,
|
|
102
|
+
]);
|
|
103
|
+
assert.ok(after.ok);
|
|
104
|
+
assert.equal((await verifyAttestationSignature(after.keys, doc, oldSig)).ok, true, "old signature still verifies");
|
|
105
|
+
assert.equal((await verifyAttestationSignature(after.keys, lateDoc, newSig)).ok, true, "new signature verifies");
|
|
106
|
+
// The retired key can no longer vouch for anything issued after it stopped signing...
|
|
107
|
+
const forged = await signAttestationDocument(oldKey.signer, lateDoc);
|
|
108
|
+
const verdict = await verifyAttestationSignature(after.keys, lateDoc, forged);
|
|
109
|
+
assert.equal(verdict.ok, false);
|
|
110
|
+
if (!verdict.ok)
|
|
111
|
+
assert.match(verdict.reason, /retired or expired/);
|
|
112
|
+
// ...and the new key cannot vouch for anything issued before it existed.
|
|
113
|
+
const early = await signAttestationDocument(newKey.signer, doc);
|
|
114
|
+
const earlyVerdict = await verifyAttestationSignature(after.keys, doc, early);
|
|
115
|
+
assert.equal(earlyVerdict.ok, false);
|
|
116
|
+
if (!earlyVerdict.ok)
|
|
117
|
+
assert.match(earlyVerdict.reason, /not yet valid/);
|
|
118
|
+
});
|
|
119
|
+
test("revocation: a revoked key's signatures stop verifying from revokedAt on, and only then", async () => {
|
|
120
|
+
const k = await keyFor("revoke");
|
|
121
|
+
const set = await buildAttestationKeySet([{ ...k.entry, status: "revoked", revokedAt: "2026-05-01T00:00:00Z" }]);
|
|
122
|
+
assert.ok(set.ok);
|
|
123
|
+
const before = { issuedAt: "2026-04-01T00:00:00Z" };
|
|
124
|
+
const after = { issuedAt: "2026-06-01T00:00:00Z" };
|
|
125
|
+
assert.equal((await verifyAttestationSignature(set.keys, before, await signAttestationDocument(k.signer, before))).ok, true);
|
|
126
|
+
assert.equal((await verifyAttestationSignature(set.keys, after, await signAttestationDocument(k.signer, after))).ok, false);
|
|
127
|
+
});
|
|
128
|
+
test("tamper detection: any change to the document, the key id, the signature or the domain is rejected", async () => {
|
|
129
|
+
const k = await keyFor("tamper");
|
|
130
|
+
const other = await keyFor("tamper-other");
|
|
131
|
+
const set = await buildAttestationKeySet([k.entry, other.entry]);
|
|
132
|
+
assert.ok(set.ok);
|
|
133
|
+
const doc = { issuedAt: "2026-10-02T14:03:50Z", subject: { buildId: "bld_1" }, counts: [1, 2, 3] };
|
|
134
|
+
const sig = await signAttestationDocument(k.signer, doc);
|
|
135
|
+
assert.deepEqual(await verifyAttestationSignature(set.keys, doc, sig), { ok: true, keyId: k.kid });
|
|
136
|
+
const mutated = [
|
|
137
|
+
["field edited", { ...doc, subject: { buildId: "bld_2" } }],
|
|
138
|
+
["array element edited", { ...doc, counts: [1, 2, 4] }],
|
|
139
|
+
["field added", { ...doc, extra: 1 }],
|
|
140
|
+
["issuedAt moved", { ...doc, issuedAt: "2026-10-02T14:03:51Z" }],
|
|
141
|
+
];
|
|
142
|
+
for (const [name, d] of mutated)
|
|
143
|
+
assert.equal((await verifyAttestationSignature(set.keys, d, sig)).ok, false, name);
|
|
144
|
+
assert.equal((await verifyAttestationSignature(set.keys, doc, { ...sig, keyId: other.kid })).ok, false, "claimed under another key");
|
|
145
|
+
assert.equal((await verifyAttestationSignature(set.keys, doc, { ...sig, keyId: "0".repeat(16) })).ok, false, "unknown key");
|
|
146
|
+
const flipped = Buffer.from(sig.signature, "base64");
|
|
147
|
+
flipped[3] = flipped[3] ^ 1;
|
|
148
|
+
assert.equal((await verifyAttestationSignature(set.keys, doc, { ...sig, signature: flipped.toString("base64") })).ok, false);
|
|
149
|
+
assert.equal((await verifyAttestationSignature(set.keys, doc, { ...sig, signature: "%%%" })).ok, false, "garbage never throws");
|
|
150
|
+
assert.equal((await verifyAttestationSignature(set.keys, doc, { ...sig, signature: "" })).ok, false);
|
|
151
|
+
// Signing refuses a document without a valid issuedAt.
|
|
152
|
+
await assert.rejects(signAttestationDocument(k.signer, { issuedAt: "yesterday" }));
|
|
153
|
+
});
|
|
154
|
+
test("domain separation: a checkpoint signature is not an attestation signature, and vice versa", async () => {
|
|
155
|
+
const k = await keyFor("domains");
|
|
156
|
+
const set = await buildAttestationKeySet([k.entry]);
|
|
157
|
+
assert.ok(set.ok);
|
|
158
|
+
const cpSig = await signAuditCheckpoint(k.signer, statement);
|
|
159
|
+
const checkpoint = { orgId: statement.orgId, seq: statement.seq, hash: statement.hash, issuedAt: statement.issuedAt, keyId: k.kid, signature: cpSig };
|
|
160
|
+
assert.deepEqual(await verifyAuditCheckpoint(set.keys, checkpoint), { ok: true, keyId: k.kid });
|
|
161
|
+
// The checkpoint statement signed under the attestation domain is not a valid checkpoint signature.
|
|
162
|
+
const wrongDomain = await signWithDomain(k.signer, ATTESTATION_DOMAIN, statement);
|
|
163
|
+
assert.equal((await verifyAuditCheckpoint(set.keys, { ...checkpoint, signature: wrongDomain })).ok, false);
|
|
164
|
+
// And a checkpoint signature does not verify as an attestation signature over the same JSON.
|
|
165
|
+
assert.equal((await verifyWithDomain(set.keys, { domain: ATTESTATION_DOMAIN, value: statement, keyId: k.kid, signature: cpSig, issuedAt: statement.issuedAt })).ok, false);
|
|
166
|
+
// Checkpoint field tampering, and key-window enforcement against the checkpoint's own issuedAt.
|
|
167
|
+
for (const bad of [{ seq: 8 }, { hash: "cd".repeat(32) }, { orgId: "org_evil" }, { issuedAt: "2026-10-02T14:03:51Z" }]) {
|
|
168
|
+
assert.equal((await verifyAuditCheckpoint(set.keys, { ...checkpoint, ...bad })).ok, false, JSON.stringify(bad));
|
|
169
|
+
}
|
|
170
|
+
const expired = await buildAttestationKeySet([{ ...k.entry, notAfter: "2026-10-01T00:00:00Z", status: "retired" }]);
|
|
171
|
+
assert.ok(expired.ok);
|
|
172
|
+
assert.equal((await verifyAuditCheckpoint(expired.keys, checkpoint)).ok, false);
|
|
173
|
+
assert.equal((await verifyAuditCheckpoint(set.keys, { ...checkpoint, hash: "nope" })).ok, false, "a malformed checkpoint is rejected, not thrown");
|
|
174
|
+
});
|
|
175
|
+
// ---------------------------------------------------------------- loading the signer
|
|
176
|
+
test("loadAttestationSigner: no secret is reported as not_configured, never as a signer", async () => {
|
|
177
|
+
const k = await keyFor("signer-none");
|
|
178
|
+
for (const secret of [undefined, null, "", " \n"]) {
|
|
179
|
+
assert.deepEqual(await loadAttestationSigner(secret, [k.entry]), { state: "not_configured" });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
test("loadAttestationSigner: a valid, published, active key is ready, and what it signs verifies against the published list", async () => {
|
|
183
|
+
const k = await keyFor("signer-ready");
|
|
184
|
+
const now = new Date("2026-10-02T00:00:00Z");
|
|
185
|
+
const secret = Buffer.from(k.pkcs8).toString("base64");
|
|
186
|
+
const res = await loadAttestationSigner(secret, [k.entry], now);
|
|
187
|
+
assert.equal(res.state, "ready");
|
|
188
|
+
if (res.state !== "ready")
|
|
189
|
+
return;
|
|
190
|
+
assert.equal(res.keyId, k.kid, "the kid is derived from the secret, not configured");
|
|
191
|
+
const set = await buildAttestationKeySet([k.entry]);
|
|
192
|
+
assert.ok(set.ok);
|
|
193
|
+
const sig = await signAuditCheckpoint(res.signer, statement);
|
|
194
|
+
assert.equal((await verifyAuditCheckpoint(set.keys, { orgId: statement.orgId, seq: statement.seq, hash: statement.hash, issuedAt: statement.issuedAt, keyId: res.keyId, signature: sig })).ok, true);
|
|
195
|
+
// PEM armor and line wrapping, as `openssl pkey` prints it, are accepted.
|
|
196
|
+
const pem = `-----BEGIN PRIVATE KEY-----\n${secret.replace(/(.{64})/g, "$1\n")}\n-----END PRIVATE KEY-----\n`;
|
|
197
|
+
assert.equal((await loadAttestationSigner(pem, [k.entry], now)).state, "ready");
|
|
198
|
+
assert.equal((await loadAttestationSigner(` ${secret}\n`, [k.entry], now)).state, "ready");
|
|
199
|
+
});
|
|
200
|
+
test("loadAttestationSigner: refuses (invalid) anything that is not an Ed25519 PKCS#8 key, without echoing it", async () => {
|
|
201
|
+
const k = await keyFor("signer-invalid");
|
|
202
|
+
const rsa = generateKeyPairSync("rsa", { modulusLength: 2048 }).privateKey.export({ format: "der", type: "pkcs8" }).toString("base64");
|
|
203
|
+
const x25519 = generateKeyPairSync("x25519").privateKey.export({ format: "der", type: "pkcs8" }).toString("base64");
|
|
204
|
+
const rawSeed = Buffer.from(seedFor("signer-invalid")).toString("base64");
|
|
205
|
+
const secrets = ["not base64 at all!", rsa, x25519, rawSeed, Buffer.from(k.pkcs8).toString("base64").slice(0, 40)];
|
|
206
|
+
for (const secret of secrets) {
|
|
207
|
+
const res = await loadAttestationSigner(secret, [k.entry]);
|
|
208
|
+
assert.equal(res.state, "invalid", secret.slice(0, 20));
|
|
209
|
+
assert.ok(!JSON.stringify(res).includes(secret), "the reason never contains the secret");
|
|
210
|
+
}
|
|
211
|
+
// A broken published list is a configuration error too.
|
|
212
|
+
const res = await loadAttestationSigner(Buffer.from(k.pkcs8).toString("base64"), [{ ...k.entry, kid: "1".repeat(16) }]);
|
|
213
|
+
assert.equal(res.state, "invalid");
|
|
214
|
+
});
|
|
215
|
+
test("loadAttestationSigner: the signer re-checks the key's window at each issuedAt, so nothing is signed that could never verify", async () => {
|
|
216
|
+
const k = await keyFor("signer-window", { notAfter: "2026-10-02T12:00:00Z" });
|
|
217
|
+
const secret = Buffer.from(k.pkcs8).toString("base64");
|
|
218
|
+
const res = await loadAttestationSigner(secret, [k.entry], new Date("2026-10-02T11:59:59Z"));
|
|
219
|
+
assert.equal(res.state, "ready");
|
|
220
|
+
if (res.state !== "ready")
|
|
221
|
+
return;
|
|
222
|
+
assert.equal(res.signer.problemAt?.("2026-10-02T11:59:59Z"), null);
|
|
223
|
+
assert.match(res.signer.problemAt?.("2026-10-02T12:00:01Z") ?? "", /retired or expired/);
|
|
224
|
+
await assert.rejects(signAttestationDocument(res.signer, { issuedAt: "2026-10-02T12:00:01Z", subject: "x" }), /cannot sign a document issued at 2026-10-02T12:00:01Z/);
|
|
225
|
+
assert.equal((await signAttestationDocument(res.signer, { issuedAt: "2026-10-02T11:59:59Z", subject: "x" })).keyId, k.kid);
|
|
226
|
+
});
|
|
227
|
+
test("loadAttestationSigner: a key the published list does not vouch for right now is unpublished (nothing is signed)", async () => {
|
|
228
|
+
const k = await keyFor("signer-unpublished");
|
|
229
|
+
const stranger = await keyFor("signer-stranger");
|
|
230
|
+
const secret = Buffer.from(k.pkcs8).toString("base64");
|
|
231
|
+
const now = new Date("2026-10-02T00:00:00Z");
|
|
232
|
+
const cases = [
|
|
233
|
+
["not listed at all", [stranger.entry], /not in the published key list/],
|
|
234
|
+
["empty published list", [], /not in the published key list/],
|
|
235
|
+
["listed as retired", [{ ...k.entry, status: "retired", notAfter: "2026-09-01T00:00:00Z" }], /listed as retired/],
|
|
236
|
+
["listed as revoked", [{ ...k.entry, status: "revoked", revokedAt: "2026-09-01T00:00:00Z" }], /listed as revoked/],
|
|
237
|
+
["not valid yet", [{ ...k.entry, notBefore: "2027-01-01T00:00:00Z" }], /not yet valid/],
|
|
238
|
+
["expired though still marked active", [{ ...k.entry, notAfter: "2026-09-01T00:00:00Z" }], /retired or expired/],
|
|
239
|
+
];
|
|
240
|
+
for (const [name, published, re] of cases) {
|
|
241
|
+
const res = await loadAttestationSigner(secret, published, now);
|
|
242
|
+
assert.equal(res.state, "unpublished", name);
|
|
243
|
+
if (res.state === "unpublished") {
|
|
244
|
+
assert.match(res.reason, re, name);
|
|
245
|
+
assert.equal(res.keyId, k.kid);
|
|
246
|
+
assert.ok(!res.reason.includes(secret));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
//# sourceMappingURL=attestation-signing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation-signing.test.js","sourceRoot":"","sources":["../src/attestation-signing.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation-workerd.test.d.ts","sourceRoot":"","sources":["../src/attestation-workerd.test.ts"],"names":[],"mappings":""}
|