@andrewheberle/serverless-ssh-ca 0.17.5 → 0.17.6

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.
Files changed (48) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +268 -0
  3. package/dist/api/v3/schema.js +2 -1
  4. package/dist/proof.js +1 -1
  5. package/dist/sshsig/formats.js +1 -1
  6. package/dist/utils.d.ts +2 -1
  7. package/dist/utils.js +1 -2
  8. package/package.json +8 -13
  9. package/dist/src/api/v3/index.d.ts +0 -1
  10. package/dist/src/api/v3/index.js +0 -263
  11. package/dist/src/api/v3/schema.d.ts +0 -217
  12. package/dist/src/api/v3/schema.js +0 -216
  13. package/dist/src/certificate.d.ts +0 -30
  14. package/dist/src/certificate.js +0 -154
  15. package/dist/src/db.d.ts +0 -36
  16. package/dist/src/db.js +0 -111
  17. package/dist/src/index.d.ts +0 -6
  18. package/dist/src/index.js +0 -8
  19. package/dist/src/krl.d.ts +0 -39
  20. package/dist/src/krl.js +0 -317
  21. package/dist/src/logger.d.ts +0 -2
  22. package/dist/src/logger.js +0 -3
  23. package/dist/src/proof.d.ts +0 -51
  24. package/dist/src/proof.js +0 -154
  25. package/dist/src/router.d.ts +0 -14
  26. package/dist/src/router.js +0 -22
  27. package/dist/src/sshsig/armor.d.ts +0 -7
  28. package/dist/src/sshsig/armor.js +0 -18
  29. package/dist/src/sshsig/formats.d.ts +0 -52
  30. package/dist/src/sshsig/formats.js +0 -174
  31. package/dist/src/sshsig/index.d.ts +0 -15
  32. package/dist/src/sshsig/index.js +0 -31
  33. package/dist/src/sshsig/reader.d.ts +0 -20
  34. package/dist/src/sshsig/reader.js +0 -54
  35. package/dist/src/sshsig/sig.d.ts +0 -16
  36. package/dist/src/sshsig/sig.js +0 -1
  37. package/dist/src/sshsig/sig_parser.d.ts +0 -9
  38. package/dist/src/sshsig/sig_parser.js +0 -83
  39. package/dist/src/sshsig/verifier.d.ts +0 -2
  40. package/dist/src/sshsig/verifier.js +0 -37
  41. package/dist/src/sshsig/writer.d.ts +0 -17
  42. package/dist/src/sshsig/writer.js +0 -51
  43. package/dist/src/types.d.ts +0 -42
  44. package/dist/src/types.js +0 -20
  45. package/dist/src/utils.d.ts +0 -76
  46. package/dist/src/utils.js +0 -370
  47. package/dist/src/verify.d.ts +0 -6
  48. package/dist/src/verify.js +0 -17
package/dist/src/krl.js DELETED
@@ -1,317 +0,0 @@
1
- // ── Constants ────────────────────────────────────────────────────────────────
2
- const KRL_MAGIC = 0x5353484b524c0a00n; // "SSHKRL\n\0"
3
- const KRL_FORMAT_VERSION = 1;
4
- const KRL_SECTION_CERTIFICATES = 1;
5
- const KRL_SECTION_CERT_SERIAL_LIST = 0x20;
6
- const KRL_SECTION_CERT_KEY_ID = 0x23;
7
- // ── Low-level encoding helpers ───────────────────────────────────────────────
8
- /** Encode a uint32 big-endian. */
9
- function encodeUint32(value) {
10
- const buf = new Uint8Array(4);
11
- new DataView(buf.buffer).setUint32(0, value, false);
12
- return buf;
13
- }
14
- /** Encode a uint64 big-endian from a bigint. */
15
- function encodeUint64(value) {
16
- const buf = new Uint8Array(8);
17
- new DataView(buf.buffer).setBigUint64(0, value, false);
18
- return buf;
19
- }
20
- /**
21
- * Encode an SSH "string" (RFC 4251 §5): uint32 length followed by raw bytes.
22
- * Accepts either a Uint8Array (raw bytes) or a JS string (encoded as UTF-8).
23
- */
24
- function encodeString(value) {
25
- const bytes = typeof value === "string" ? new TextEncoder().encode(value) : value;
26
- const out = new Uint8Array(4 + bytes.length);
27
- new DataView(out.buffer).setUint32(0, bytes.length, false);
28
- out.set(bytes, 4);
29
- return out;
30
- }
31
- /** Concatenate an arbitrary number of Uint8Arrays into one. */
32
- function concat(...parts) {
33
- const total = parts.reduce((n, p) => n + p.length, 0);
34
- const out = new Uint8Array(total);
35
- let offset = 0;
36
- for (const part of parts) {
37
- out.set(part, offset);
38
- offset += part.length;
39
- }
40
- return out;
41
- }
42
- /**
43
- * Wrap bytes as a KRL section: byte section_type + string section_data.
44
- * The spec defines each section as: byte type | string data
45
- */
46
- function encodeSection(sectionType, data) {
47
- return concat(new Uint8Array([sectionType]), encodeString(data));
48
- }
49
- // ── CA public-key extraction ─────────────────────────────────────────────────
50
- /**
51
- * Extract the SSH wire-format public key bytes from an sshpk PrivateKey.
52
- *
53
- * sshpk's "ssh" format produces a string like:
54
- * ssh-ed25519 AAAA...base64... [comment]
55
- *
56
- * The base64 blob *is* the SSH wire encoding of the public key, so we just
57
- * decode it. This works for both Ed25519 and ECDSA keys.
58
- */
59
- function caKeyWireBytes(caKey) {
60
- const sshString = caKey.toPublic().toString("ssh");
61
- // Format: "<type> <base64> [<comment>]" — we want the second token.
62
- const base64 = sshString.trim().split(/\s+/)[1];
63
- if (!base64) {
64
- throw new Error("Unexpected sshpk 'ssh' format output: " + sshString);
65
- }
66
- const binary = atob(base64);
67
- const bytes = new Uint8Array(binary.length);
68
- for (let i = 0; i < binary.length; i++) {
69
- bytes[i] = binary.charCodeAt(i);
70
- }
71
- return bytes;
72
- }
73
- // ── Constants ────────────────────────────────────────────────────────────────
74
- const SSHSIG_MAGIC = new TextEncoder().encode("SSHSIG");
75
- const SIG_VERSION = 1;
76
- const NAMESPACE = "krl@com.github.serverless-ssh-ca.andrewheberle";
77
- const HASH_ALGORITHM = "sha512";
78
- // ── Helpers ──────────────────────────────────────────────────────────────────
79
- /**
80
- * PEM string → raw DER bytes (strips header/footer and decodes base64).
81
- * Works for both PKCS8 ("-----BEGIN PRIVATE KEY-----") and any other PEM type.
82
- */
83
- function pemToDer(pem) {
84
- const lines = pem
85
- .trim()
86
- .split("\n")
87
- .filter((l) => !l.startsWith("-----"));
88
- const binary = atob(lines.join(""));
89
- const bytes = new Uint8Array(binary.length);
90
- for (let i = 0; i < binary.length; i++) {
91
- bytes[i] = binary.charCodeAt(i);
92
- }
93
- return bytes;
94
- }
95
- /**
96
- * Import an sshpk PrivateKey into a WebCrypto CryptoKey.
97
- * Supports Ed25519 and ECDSA (P-256 / P-384 / P-521).
98
- */
99
- async function importPrivateKey(caKey) {
100
- const pkcs8Pem = caKey.toString("pkcs8");
101
- const der = pemToDer(pkcs8Pem);
102
- const type = caKey.type; // "ed25519" | "ecdsa" | etc.
103
- const curve = caKey.curve;
104
- if (type === "ed25519") {
105
- const cryptoKey = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, false, ["sign"]);
106
- return {
107
- cryptoKey,
108
- sigAlgo: "ssh-ed25519",
109
- webCryptoAlgo: { name: "Ed25519" },
110
- };
111
- }
112
- else if (type === "ecdsa") {
113
- let namedCurve;
114
- let sigAlgo;
115
- let hash;
116
- if (curve === "nistp256") {
117
- namedCurve = "P-256";
118
- sigAlgo = "ecdsa-sha2-nistp256";
119
- hash = "SHA-256";
120
- }
121
- else if (curve === "nistp384") {
122
- namedCurve = "P-384";
123
- sigAlgo = "ecdsa-sha2-nistp384";
124
- hash = "SHA-384";
125
- }
126
- else if (curve === "nistp521") {
127
- namedCurve = "P-521";
128
- sigAlgo = "ecdsa-sha2-nistp521";
129
- hash = "SHA-512";
130
- }
131
- else {
132
- throw new Error(`Unsupported ECDSA curve: ${curve}`);
133
- }
134
- const cryptoKey = await crypto.subtle.importKey("pkcs8", der, { name: "ECDSA", namedCurve }, false, ["sign"]);
135
- return {
136
- cryptoKey,
137
- sigAlgo,
138
- webCryptoAlgo: { name: "ECDSA", hash: { name: hash } },
139
- };
140
- }
141
- else {
142
- throw new Error(`Unsupported key type: ${type}`);
143
- }
144
- }
145
- /**
146
- * Convert a WebCrypto ECDSA signature (IEEE P1363: raw r||s) to the SSH wire
147
- * encoding: string(algo) + string(mpint(r) + mpint(s)).
148
- *
149
- * SSH mpints are big-endian, with a leading 0x00 byte if the high bit is set.
150
- */
151
- function p1363ToSshEcdsaSig(p1363, sigAlgo) {
152
- const half = p1363.length / 2;
153
- const r = p1363.slice(0, half);
154
- const s = p1363.slice(half);
155
- function encodeMpint(n) {
156
- // Strip leading zero bytes, then re-pad if high bit set.
157
- let start = 0;
158
- while (start < n.length - 1 && n[start] === 0)
159
- start++;
160
- const trimmed = n.slice(start);
161
- const padded = trimmed[0] & 0x80 ? concat(new Uint8Array([0x00]), trimmed) : trimmed;
162
- return encodeString(padded);
163
- }
164
- const innerSig = concat(encodeMpint(r), encodeMpint(s));
165
- return concat(encodeString(sigAlgo), encodeString(innerSig));
166
- }
167
- /**
168
- * Wrap an Ed25519 WebCrypto signature into SSH wire encoding:
169
- * string(algo) + string(sig_bytes)
170
- */
171
- function wrapEd25519Sig(sig) {
172
- return concat(encodeString("ssh-ed25519"), encodeString(sig));
173
- }
174
- /**
175
- * Base64-encode bytes and wrap in SSH signature armor at 76 chars per line.
176
- */
177
- function armorSignature(bytes) {
178
- let b64 = btoa(String.fromCharCode(...bytes));
179
- const lines = [];
180
- for (let i = 0; i < b64.length; i += 76) {
181
- lines.push(b64.slice(i, i + 76));
182
- }
183
- return [
184
- "-----BEGIN SSH SIGNATURE-----",
185
- ...lines,
186
- "-----END SSH SIGNATURE-----",
187
- ].join("\n");
188
- }
189
- // ── KRLBuilder ───────────────────────────────────────────────────────────────
190
- export class KRLBuilder {
191
- _caKey;
192
- _serials = [];
193
- _keyIds = [];
194
- _krl = null;
195
- constructor(caKey) {
196
- this._caKey = caKey;
197
- }
198
- /**
199
- * Add certificate serial numbers to be revoked.
200
- * May be called multiple times all serials are merged into one section.
201
- *
202
- * This must not be called after generate() has been called
203
- */
204
- addSerials(serials) {
205
- if (this._krl !== null) {
206
- throw new Error("addSerials cannot be called after KRL has been generated");
207
- }
208
- this._serials.push(...serials);
209
- return this;
210
- }
211
- /**
212
- * Add certificate key ID strings to be revoked.
213
- * May be called multiple times all key IDs are merged into one section.
214
- *
215
- * This must not be called after generate() has been called
216
- */
217
- addKeyIds(keyIds) {
218
- if (this._krl !== null) {
219
- throw new Error("addKeyIds cannot be called after KRL has been generated");
220
- }
221
- this._keyIds.push(...keyIds);
222
- return this;
223
- }
224
- /**
225
- * Generate the binary KRL
226
- *
227
- * @param krlVersion Optional KRL version number (default: 1).
228
- * @param comment Optional comment string embedded in the header.
229
- */
230
- generate(krlVersion = 1n, comment = "") {
231
- if (this._krl !== null) {
232
- return this._krl;
233
- }
234
- // ── Header ──────────────────────────────────────────────────────────────
235
- const generatedDate = BigInt(Math.floor(Date.now() / 1000));
236
- const header = concat(encodeUint64(KRL_MAGIC), encodeUint32(KRL_FORMAT_VERSION), encodeUint64(krlVersion), encodeUint64(generatedDate), encodeUint64(0n), // flags — none defined
237
- encodeString(""), // reserved
238
- encodeString(comment));
239
- // ── Certificate sub-sections ─────────────────────────────────────────────
240
- const certSubSections = [];
241
- // KRL_SECTION_CERT_SERIAL_LIST
242
- if (this._serials.length > 0) {
243
- const serialBytes = concat(...this._serials.map(encodeUint64));
244
- certSubSections.push(encodeSection(KRL_SECTION_CERT_SERIAL_LIST, serialBytes));
245
- }
246
- // KRL_SECTION_CERT_KEY_ID
247
- if (this._keyIds.length > 0) {
248
- const keyIdBytes = concat(...this._keyIds.map((id) => encodeString(id)));
249
- certSubSections.push(encodeSection(KRL_SECTION_CERT_KEY_ID, keyIdBytes));
250
- }
251
- // ── KRL_SECTION_CERTIFICATES wrapper ────────────────────────────────────
252
- //
253
- // Format (inside the outer section's string data):
254
- // string ca_key
255
- // string reserved
256
- // <one or more cert sub-sections>
257
- //
258
- const certSectionData = concat(encodeString(caKeyWireBytes(this._caKey)), // ca_key
259
- encodeString(""), // reserved
260
- ...certSubSections);
261
- const certificatesSection = encodeSection(KRL_SECTION_CERTIFICATES, certSectionData);
262
- // ── Assemble final KRL ───────────────────────────────────────────────────
263
- const krl = concat(header, certificatesSection);
264
- this._krl = krl.buffer;
265
- return this._krl;
266
- }
267
- /**
268
- * Return the SSHSIG signature of the KRL.
269
- *
270
- * This must be called after generate()
271
- *
272
- * The returned string can be verified with ssh-keygen:
273
- * echo -n "$KRL_BYTES" | ssh-keygen -Y verify -f allowed_signers \
274
- * -I <identity> -n file -s <sig_file>
275
- */
276
- async signature() {
277
- let krl;
278
- if (this._krl === null) {
279
- krl = this.generate();
280
- }
281
- else {
282
- krl = this._krl;
283
- }
284
- const { cryptoKey, sigAlgo, webCryptoAlgo } = await importPrivateKey(this._caKey);
285
- // 1. Hash the KRL bytes with SHA-512.
286
- const krlHash = new Uint8Array(await crypto.subtle.digest("SHA-512", krl));
287
- // 2. Build the signed data blob.
288
- //
289
- // byte[6] "SSHSIG"
290
- // string namespace
291
- // string reserved (empty)
292
- // string hash_algorithm
293
- // string H(message)
294
- const signedData = concat(SSHSIG_MAGIC, encodeString(NAMESPACE), encodeString(""), // reserved
295
- encodeString(HASH_ALGORITHM), encodeString(krlHash));
296
- // 3. Sign the blob.
297
- const rawSig = new Uint8Array(await crypto.subtle.sign(webCryptoAlgo, cryptoKey, signedData));
298
- // 4. Encode the signature into SSH wire format.
299
- const sshSig = sigAlgo === "ssh-ed25519"
300
- ? wrapEd25519Sig(rawSig)
301
- : p1363ToSshEcdsaSig(rawSig, sigAlgo);
302
- // 5. Assemble the outer SSHSIG blob.
303
- //
304
- // byte[6] "SSHSIG"
305
- // uint32 SIG_VERSION (1)
306
- // string publickey
307
- // string namespace
308
- // string reserved (empty)
309
- // string hash_algorithm
310
- // string signature
311
- const publicKeyWire = caKeyWireBytes(this._caKey);
312
- const sigBlob = concat(SSHSIG_MAGIC, encodeUint32(SIG_VERSION), encodeString(publicKeyWire), encodeString(NAMESPACE), encodeString(""), // reserved
313
- encodeString(HASH_ALGORITHM), encodeString(sshSig));
314
- // 6. Armor and return.
315
- return armorSignature(sigBlob);
316
- }
317
- }
@@ -1,2 +0,0 @@
1
- import { Logger } from "@andrewheberle/ts-slog";
2
- export declare const logger: Logger;
@@ -1,3 +0,0 @@
1
- import { Logger, LogLevel } from "@andrewheberle/ts-slog";
2
- import { env } from "cloudflare:workers";
3
- export const logger = new Logger({ minLevel: env.LOG_LEVEL === "debug" ? LogLevel.Debug : LogLevel.Info });
@@ -1,51 +0,0 @@
1
- import { Fingerprint, Key } from "sshpk";
2
- import { Sig } from "./sshsig/sig";
3
- import { Logger } from "@andrewheberle/ts-slog";
4
- export declare class PossessionParseError extends Error {
5
- constructor(message: string, cause?: unknown);
6
- }
7
- /**
8
- * ProofOfPossession is used to check/enforce proof of possession for a SSH
9
- * private key.
10
- *
11
- * This is used to verify that the requester of a certificate actually
12
- * possesses the private key corresponding to the public key in the certificate
13
- * request.
14
- *
15
- * The proof of possession is a signed message containing a timestamp and the
16
- * fingerprint of the public key.
17
- *
18
- * The signature is made with the private key corresponding to the public key
19
- * in the certificate request and is a BASE64 encoded SSHSIG signature.
20
- */
21
- export declare class ProofOfPossession {
22
- readonly timestamp: number;
23
- readonly fingerprint: Fingerprint;
24
- readonly signature: Sig;
25
- readonly signaturePubkey: Key;
26
- readonly logger: Logger;
27
- private readonly data;
28
- constructor(proof: string, options?: {
29
- from?: number;
30
- logger?: Logger;
31
- });
32
- /**
33
- *
34
- * @returns true if signature is valid, false otherwise
35
- */
36
- verify(namespace?: string): Promise<boolean>;
37
- /**
38
- *
39
- * @param keys array of keys to verify finterprint against
40
- * @returns true if the proof of possession fingerprint matches any of the provided keys, false otherwise
41
- */
42
- matches(key: Key): boolean;
43
- matches(...keys: Key[]): boolean;
44
- }
45
- export declare class PossessionMatchError extends Error {
46
- constructor(message: string, cause?: unknown);
47
- }
48
- export declare class RenewalProofOfPossession extends ProofOfPossession {
49
- matches(key: Key): never;
50
- matches(...keys: Key[]): boolean;
51
- }
package/dist/src/proof.js DELETED
@@ -1,154 +0,0 @@
1
- import { env } from "cloudflare:workers";
2
- import { ms } from "itty-time";
3
- import { FingerprintFormatError, parseFingerprint, parseKey } from "sshpk";
4
- import { verify } from "./sshsig";
5
- import { parse } from "./sshsig/sig_parser";
6
- import { group, Logger, LogLevel } from "@andrewheberle/ts-slog";
7
- const Namespace = "proof-of-possession@com.github.serverless-ssh-ca.andrewheberle";
8
- export class PossessionParseError extends Error {
9
- constructor(message, cause) {
10
- super(message);
11
- this.name = "PossessionParseError";
12
- this.cause = cause;
13
- // This is necessary for proper stack trace in TypeScript
14
- Object.setPrototypeOf(this, PossessionParseError.prototype);
15
- }
16
- }
17
- /**
18
- * ProofOfPossession is used to check/enforce proof of possession for a SSH
19
- * private key.
20
- *
21
- * This is used to verify that the requester of a certificate actually
22
- * possesses the private key corresponding to the public key in the certificate
23
- * request.
24
- *
25
- * The proof of possession is a signed message containing a timestamp and the
26
- * fingerprint of the public key.
27
- *
28
- * The signature is made with the private key corresponding to the public key
29
- * in the certificate request and is a BASE64 encoded SSHSIG signature.
30
- */
31
- export class ProofOfPossession {
32
- timestamp;
33
- fingerprint;
34
- signature;
35
- signaturePubkey;
36
- logger;
37
- data;
38
- constructor(proof, options) {
39
- this.logger = options?.logger !== undefined ? options.logger : new Logger({ minLevel: LogLevel.None });
40
- const parts = proof.split(".");
41
- if (parts.length !== 3) {
42
- throw new PossessionParseError("invalid proof of possession format");
43
- }
44
- const [timestampStr, fingerprintString, signatureBase64] = parts;
45
- // verify timestamp
46
- const timestamp = parseInt(timestampStr, 10);
47
- if (isNaN(timestamp)) {
48
- throw new PossessionParseError("timestamp was not a number");
49
- }
50
- const now = options?.from !== undefined ? options?.from : Date.now();
51
- const age = now - timestamp;
52
- const skew = ms(env.CERTIFICATE_REQUEST_TIME_SKEW_MAX);
53
- if (age > skew) {
54
- throw new PossessionParseError("timestamp too old");
55
- }
56
- if (age + skew < 0) {
57
- throw new PossessionParseError("timestamp was from the future");
58
- }
59
- try {
60
- // parse fingerprint
61
- const fingerprint = parseFingerprint(fingerprintString);
62
- if (fingerprint === undefined) {
63
- throw new PossessionParseError("fingerprint did not parse");
64
- }
65
- // convert siganture from base64
66
- const signature = Buffer.from(signatureBase64, "base64").toString();
67
- // set values
68
- this.timestamp = timestamp;
69
- try {
70
- this.signature = parse(signature);
71
- }
72
- catch (err) {
73
- throw new PossessionParseError("proof of possession signature could not be parsed", err);
74
- }
75
- this.fingerprint = fingerprint;
76
- this.data = `${timestamp}.${fingerprintString}`;
77
- this.signaturePubkey = parseKey(this.signature.publickey.toString());
78
- // ensure the provided fingerprint matches the signature public key
79
- if (!this.fingerprint.matches(this.signaturePubkey)) {
80
- throw new PossessionParseError("proof of possession fingerprint does not match signature public key");
81
- }
82
- this.logger.debug("constructor completed", ...group("this", "timestamp", this.timestamp, "fingerprint", this.fingerprint.toString("base64"), "signaturePubkey.fingerprint()", this.signaturePubkey.fingerprint().toString("base64")));
83
- }
84
- catch (err) {
85
- switch (true) {
86
- case (err instanceof FingerprintFormatError):
87
- throw new PossessionParseError("proof of possession fingerprint was an invalid format", err);
88
- default:
89
- throw err;
90
- }
91
- }
92
- }
93
- /**
94
- *
95
- * @returns true if signature is valid, false otherwise
96
- */
97
- async verify(namespace) {
98
- return await verify(this.signature, this.data, { namespace: namespace || Namespace });
99
- }
100
- matches(...keys) {
101
- this.logger.debug("matches() called", "in", "ProofOfPossession", "keys", keys.length);
102
- try {
103
- for (const key of keys) {
104
- // confirm proof of possession fingerprint matches keys
105
- this.logger.debug("this.fingerprint.matches()", "in", "ProofOfPossession", "fingerprint", this.fingerprint.toString("base64"), "key", key.toString("ssh"));
106
- if (!this.fingerprint.matches(key))
107
- return false;
108
- // also confirm key used to sign proof of possession matches
109
- this.logger.debug("this.signaturePubkey.fingerprint().matches()", "in", "ProofOfPossession", "signaturePubkey", this.signaturePubkey.fingerprint().toString("base64"), "key", key.toString("ssh"));
110
- if (!this.signaturePubkey.fingerprint().matches(key))
111
- return false;
112
- }
113
- this.logger.debug("matches() completed", "in", "ProofOfPossession", "keys", keys.length);
114
- return true;
115
- }
116
- catch (err) {
117
- this.logger.error("matches() error", "in", "ProofOfPossession", "error", err);
118
- throw err;
119
- }
120
- }
121
- }
122
- export class PossessionMatchError extends Error {
123
- constructor(message, cause) {
124
- super(message);
125
- this.name = "PossessionMatchError";
126
- this.cause = cause;
127
- // This is necessary for proper stack trace in TypeScript
128
- Object.setPrototypeOf(this, PossessionMatchError.prototype);
129
- }
130
- }
131
- export class RenewalProofOfPossession extends ProofOfPossession {
132
- matches(...keys) {
133
- this.logger.debug("matches() called", "in", "RenewalProofOfPossession", "keys", keys.length);
134
- if (keys.length === 1) {
135
- throw new PossessionMatchError("must verify both public key and certificate");
136
- }
137
- try {
138
- for (const key of keys) {
139
- this.logger.debug("this.fingerprint.matches()", "in", "RenewalProofOfPossession", "fingerprint", this.fingerprint.toString("base64"), "key", key.toString("ssh"));
140
- if (!this.fingerprint.matches(key))
141
- return false;
142
- this.logger.debug("this.signaturePubkey.fingerprint().matches()", "in", "RenewalProofOfPossession", "signaturePubkey", this.signaturePubkey.fingerprint().toString("base64"), "key", key.toString("ssh"));
143
- if (!this.signaturePubkey.fingerprint().matches(key))
144
- return false;
145
- }
146
- this.logger.debug("matches() completed", "in", "RenewalProofOfPossession", "keys", keys.length);
147
- return true;
148
- }
149
- catch (err) {
150
- this.logger.error("error in matches()", "in", "RenewalProofOfPossession", "error", err);
151
- throw err;
152
- }
153
- }
154
- }
@@ -1,14 +0,0 @@
1
- import { Hono, type Context } from "hono";
2
- import { SshCaBindings } from "./types";
3
- export type CFArgs = [SshCaBindings, ExecutionContext];
4
- export type AppContext = Context<{
5
- Bindings: SshCaBindings;
6
- }>;
7
- export declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
8
- export declare const openapi: import("chanfana").HonoOpenAPIRouterType<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
9
- export declare const oidcAuth: {
10
- name: string;
11
- ref: {
12
- $ref: string;
13
- };
14
- };
@@ -1,22 +0,0 @@
1
- import { fromHono } from "chanfana";
2
- import { Hono } from "hono";
3
- import { api as apiv3 } from "./api/v3";
4
- import { HTTPException } from "hono/http-exception";
5
- import { logger } from "./logger";
6
- export const app = new Hono();
7
- // add error handling
8
- app.onError((err, c) => {
9
- if (err instanceof HTTPException) {
10
- return err.getResponse();
11
- }
12
- // Handle other errors
13
- logger.error("unexpected error from router", "error", err);
14
- return c.json({ success: false, errors: [{ code: 7000, message: "Internal Server Error" }] }, 500);
15
- });
16
- export const openapi = fromHono(app);
17
- export const oidcAuth = openapi.registry.registerComponent("securitySchemes", "oidcAuth", {
18
- type: 'http',
19
- scheme: 'bearer',
20
- bearerFormat: 'JWT',
21
- });
22
- openapi.route("/api/v3", apiv3);
@@ -1,7 +0,0 @@
1
- /**
2
- * Removes armoring from text SSH signatures and decodes it to raw bytes.
3
- *
4
- * @param {string} text SSH signature in armored format.
5
- * @returns {Uint8Array} Raw bytes of the signature.
6
- */
7
- export declare function dearmor(text: string): Uint8Array<ArrayBuffer>;
@@ -1,18 +0,0 @@
1
- /**
2
- * Removes armoring from text SSH signatures and decodes it to raw bytes.
3
- *
4
- * @param {string} text SSH signature in armored format.
5
- * @returns {Uint8Array} Raw bytes of the signature.
6
- */
7
- export function dearmor(text) {
8
- const lines = text.trim().split("\n");
9
- const first = lines.shift();
10
- if (first !== "-----BEGIN SSH SIGNATURE-----") {
11
- throw new Error("Bad header line, expected -----BEGIN SSH SIGNATURE----- got: " + first);
12
- }
13
- const last = lines.pop();
14
- if (last !== "-----END SSH SIGNATURE-----") {
15
- throw new Error("Bad trailer line, expected -----END SSH SIGNATURE----- got: " + last);
16
- }
17
- return Uint8Array.from(atob(lines.join("")).split("").map((x) => x.charCodeAt(0)));
18
- }
@@ -1,52 +0,0 @@
1
- import { Reader } from "./reader";
2
- /**
3
- * Represents an SSH public key.
4
- */
5
- export type Pubkey = {
6
- pk_algo: "ssh-rsa";
7
- e: Uint8Array;
8
- n: Uint8Array;
9
- toString(): string;
10
- } | {
11
- pk_algo: "ssh-ed25519";
12
- key: Uint8Array;
13
- toString(): string;
14
- } | {
15
- pk_algo: "ecdsa-sha2-nistp256" | "ecdsa-sha2-nistp384" | "ecdsa-sha2-nistp521";
16
- curve: string;
17
- point: Uint8Array;
18
- toString(): string;
19
- } | {
20
- pk_algo: "sk-ecdsa-sha2-nistp256@openssh.com";
21
- curve: string;
22
- point: Uint8Array;
23
- application: string;
24
- toString(): string;
25
- } | {
26
- pk_algo: "sk-ssh-ed25519@openssh.com";
27
- key: Uint8Array;
28
- application: string;
29
- toString(): string;
30
- };
31
- export declare function parsePubkey(pk_algo: string, publickey: Reader, raw_publickey: ArrayBuffer): Pubkey;
32
- export declare function convertPublicKey(publickey: Pubkey): {
33
- format: "jwk";
34
- keyData: JsonWebKey;
35
- } | {
36
- format: "raw";
37
- keyData: ArrayBuffer;
38
- };
39
- export declare function convertAlgorithm(sig_algo: string): {
40
- name: string;
41
- hash: {
42
- name: string;
43
- };
44
- namedCurve?: undefined;
45
- } | {
46
- name: string;
47
- namedCurve: string;
48
- hash: {
49
- name: string;
50
- };
51
- };
52
- export declare function convertHash(hashName: string): string;