@andrewheberle/serverless-ssh-ca 0.22.0 → 0.23.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.
Files changed (84) hide show
  1. package/README.md +55 -1
  2. package/dist/api/operations.d.ts +49 -0
  3. package/dist/api/operations.d.ts.map +1 -0
  4. package/dist/api/operations.js +175 -0
  5. package/dist/api/operations.js.map +1 -0
  6. package/dist/api/schema.d.ts +71 -0
  7. package/dist/api/schema.d.ts.map +1 -0
  8. package/dist/api/schema.js +107 -0
  9. package/dist/api/schema.js.map +1 -0
  10. package/dist/api/v3/index.d.ts.map +1 -1
  11. package/dist/api/v3/index.js +13 -202
  12. package/dist/api/v3/index.js.map +1 -1
  13. package/dist/api/v3/schema.d.ts +23 -68
  14. package/dist/api/v3/schema.d.ts.map +1 -1
  15. package/dist/api/v3/schema.js +10 -100
  16. package/dist/api/v3/schema.js.map +1 -1
  17. package/dist/api/v4/index.d.ts +8 -0
  18. package/dist/api/v4/index.d.ts.map +1 -0
  19. package/dist/api/v4/index.js +155 -0
  20. package/dist/api/v4/index.js.map +1 -0
  21. package/dist/api/v4/schema.d.ts +268 -0
  22. package/dist/api/v4/schema.d.ts.map +1 -0
  23. package/dist/api/v4/schema.js +182 -0
  24. package/dist/api/v4/schema.js.map +1 -0
  25. package/dist/certificate.d.ts +9 -6
  26. package/dist/certificate.d.ts.map +1 -1
  27. package/dist/certificate.js +55 -99
  28. package/dist/certificate.js.map +1 -1
  29. package/dist/db/index.d.ts +17 -5
  30. package/dist/db/index.d.ts.map +1 -1
  31. package/dist/db/index.js +39 -23
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/krl.d.ts +1 -1
  34. package/dist/krl.d.ts.map +1 -1
  35. package/dist/krl.js +9 -163
  36. package/dist/krl.js.map +1 -1
  37. package/dist/proof.d.ts +36 -7
  38. package/dist/proof.d.ts.map +1 -1
  39. package/dist/proof.js +53 -21
  40. package/dist/proof.js.map +1 -1
  41. package/dist/router.d.ts.map +1 -1
  42. package/dist/router.js +3 -1
  43. package/dist/router.js.map +1 -1
  44. package/dist/ssh/certificate.d.ts +93 -0
  45. package/dist/ssh/certificate.d.ts.map +1 -0
  46. package/dist/ssh/certificate.js +178 -0
  47. package/dist/ssh/certificate.js.map +1 -0
  48. package/dist/ssh/encoding.d.ts +41 -0
  49. package/dist/ssh/encoding.d.ts.map +1 -0
  50. package/dist/ssh/encoding.js +70 -0
  51. package/dist/ssh/encoding.js.map +1 -0
  52. package/dist/ssh/keys.d.ts +80 -0
  53. package/dist/ssh/keys.d.ts.map +1 -0
  54. package/dist/ssh/keys.js +249 -0
  55. package/dist/ssh/keys.js.map +1 -0
  56. package/dist/ssh/signer.d.ts +35 -0
  57. package/dist/ssh/signer.d.ts.map +1 -0
  58. package/dist/ssh/signer.js +99 -0
  59. package/dist/ssh/signer.js.map +1 -0
  60. package/dist/types.d.ts +12 -17
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/utils.d.ts +40 -21
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +87 -54
  65. package/dist/utils.js.map +1 -1
  66. package/openapi.json +1542 -1
  67. package/package.json +4 -3
  68. package/src/api/operations.ts +211 -0
  69. package/src/api/schema.ts +137 -0
  70. package/src/api/v3/index.ts +20 -248
  71. package/src/api/v3/schema.ts +26 -125
  72. package/src/api/v4/index.ts +265 -0
  73. package/src/api/v4/schema.ts +235 -0
  74. package/src/certificate.ts +62 -123
  75. package/src/db/index.ts +41 -26
  76. package/src/krl.ts +10 -195
  77. package/src/proof.ts +65 -31
  78. package/src/router.ts +3 -1
  79. package/src/ssh/certificate.ts +236 -0
  80. package/src/ssh/encoding.ts +81 -0
  81. package/src/ssh/keys.ts +294 -0
  82. package/src/ssh/signer.ts +129 -0
  83. package/src/types.ts +12 -18
  84. package/src/utils.ts +99 -69
@@ -0,0 +1,182 @@
1
+ import z from "zod";
2
+ import { refineRenewableCertificate, split, transformCertificate, transformProofOfPossession, } from "../../utils.js";
3
+ import { ConflictException, contentJson, InputValidationException, InternalServerErrorException, NotFoundException, UnauthorizedException, ForbiddenException, UnprocessableEntityException, } from "chanfana";
4
+ import { seconds } from "itty-time";
5
+ import { isRevoked } from "../../db/index.js";
6
+ import { CertificateType } from "../../db/types.js";
7
+ import { CaPublicKeyEndpointSchema, certificate, certificateTypeName, certificateTypeTag, createAccessTokenSchema, createIdentityTokenSchema, createRevocationListEndpointSchema, openapiStringByte, publicKey, } from "../schema.js";
8
+ /**
9
+ * The name of the header carrying the proof of possession of the private key
10
+ */
11
+ export const ProofHeader = "Proof-Of-Possession";
12
+ export const createProofSchema = (env) => (z.string()
13
+ .meta({
14
+ description: "Proof of possession of the private key comprising of `${timestamp}.${fingerprint}.${signature}`. The signature is a BASE64 encoded armored SSHSIG signature, made with the `request-proof-of-possession@com.github.serverless-ssh-ca.andrewheberle` namespace, of `${timestamp}.${fingerprint}.${METHOD} ${path}` where `${METHOD} ${path}` is the request the proof is for, for example `DELETE /api/v4/user/certificates/12345678901234567890`. The timestamp is in milliseconds since the Unix epoch.",
15
+ example: "1765696780805.SHA256:79mq0wtpQMTvS4+Of8VzLN0qmYWNUyTXYmqKwhEgSLs.LS0tLS1CRUdJTiBTU0ggU0lHTkFUVVJFLS0tLS0K...",
16
+ })
17
+ .transform((val, ctx) => transformProofOfPossession(env, val, ctx)));
18
+ /**
19
+ * Headers for a request authorised by a proof of possession alone
20
+ */
21
+ export const createProofHeaderSchema = (env) => (z.object({
22
+ [ProofHeader]: createProofSchema(env),
23
+ }));
24
+ /**
25
+ * Headers for a request authenticated with an access token and authorised by
26
+ * a proof of possession
27
+ */
28
+ export const createAuthenticatedHeaderSchema = (env) => (z.object({
29
+ "Authorization": createAccessTokenSchema(env),
30
+ [ProofHeader]: createProofSchema(env),
31
+ }));
32
+ export const serialParamSchema = z.object({
33
+ // a string as JSON cannot carry a bigint and numbers lose precision
34
+ // above 2^53, and without leading zeros so the path a proof is made for
35
+ // has only one form
36
+ serial: z.string()
37
+ .regex(/^(0|[1-9]\d*)$/)
38
+ .transform(BigInt)
39
+ .refine((v) => v <= 0xffffffffffffffffn, { message: "serial must fit in an unsigned 64-bit integer" })
40
+ .meta({ description: "Serial number of the certificate", example: "12345678901234567890" }),
41
+ });
42
+ const certificateResponse = {
43
+ ...contentJson(z.object({
44
+ certificate: certificate,
45
+ })
46
+ .openapi("Certificate Response"))
47
+ };
48
+ export const createCaPublicKeyEndpointSchema = () => ({
49
+ ...CaPublicKeyEndpointSchema,
50
+ operationId: "get_ca_public_key",
51
+ });
52
+ export const createGetRevocationListEndpointSchema = (certificateType) => ({
53
+ ...createRevocationListEndpointSchema(certificateType),
54
+ operationId: `get_${certificateTypeName(certificateType)}_revocation_list`,
55
+ });
56
+ export const issueUserCertificateEndpointBodySchema = (env) => (z.object({
57
+ public_key: publicKey,
58
+ identity: createIdentityTokenSchema(env),
59
+ extensions: z.array(z.string())
60
+ .default(split(env.SSH_CERTIFICATE_EXTENSIONS))
61
+ .meta({ description: "Extensions to include in the issued SSH certificate" }),
62
+ lifetime: z.int()
63
+ .min(seconds("5 minutes"))
64
+ .max(seconds(env.SSH_CERTIFICATE_LIFETIME))
65
+ .default(seconds(env.SSH_CERTIFICATE_LIFETIME))
66
+ .meta({ description: "Lifetime of issued SSH certificate" }),
67
+ })
68
+ .openapi("User Certificate Issue Request"));
69
+ export const createIssueUserCertificateEndpointSchema = (env) => ({
70
+ tags: [certificateTypeTag(CertificateType.User)],
71
+ operationId: "issue_user_certificate",
72
+ summary: "Request a user certificate",
73
+ description: `Issues a signed SSH user certificate for the provided public key. The request must be authenticated with an access token and include an identity token from the OIDC IdP for the same subject, along with a proof of possession of the private key for \`public_key\` in the \`${ProofHeader}\` header made for this request. Principals on the issued certificate are derived from the identity token.`,
74
+ security: [{ oidcAuth: [] }],
75
+ request: {
76
+ headers: createAuthenticatedHeaderSchema(env),
77
+ body: contentJson(issueUserCertificateEndpointBodySchema(env)),
78
+ },
79
+ responses: {
80
+ "200": {
81
+ description: "SSH User Certificate issued successfully",
82
+ ...certificateResponse,
83
+ },
84
+ ...UnauthorizedException.schema(),
85
+ ...ForbiddenException.schema(),
86
+ ...InputValidationException.schema(),
87
+ ...UnprocessableEntityException.schema(),
88
+ ...InternalServerErrorException.schema(),
89
+ }
90
+ });
91
+ export const issueHostCertificateEndpointBodySchema = (env) => (z.object({
92
+ public_key: publicKey,
93
+ identity: createIdentityTokenSchema(env),
94
+ principals: z.array(z.string()).min(1)
95
+ .meta({ description: "List of principals to include on the issued certificate" }),
96
+ lifetime: z.int()
97
+ .min(seconds("24 hours"))
98
+ .max(seconds(env.SSH_HOST_CERTIFICATE_LIFETIME))
99
+ .default(seconds(env.SSH_HOST_CERTIFICATE_LIFETIME))
100
+ .meta({ description: "Lifetime of issued Host SSH certificate" }),
101
+ })
102
+ .openapi("Host Certificate Issue Request"));
103
+ export const createIssueHostCertificateEndpointSchema = (env) => ({
104
+ tags: [certificateTypeTag(CertificateType.Host)],
105
+ operationId: "issue_host_certificate",
106
+ summary: "Request a host certificate",
107
+ description: `Issues a signed SSH host certificate for the provided public key and principals. The request must be authenticated with an access token and include an identity token from the OIDC IdP for the same subject, along with a proof of possession of the private key for \`public_key\` in the \`${ProofHeader}\` header made for this request. The user must be permitted to request host certificates by email address or role.`,
108
+ security: [{ oidcAuth: [] }],
109
+ request: {
110
+ headers: createAuthenticatedHeaderSchema(env),
111
+ body: contentJson(issueHostCertificateEndpointBodySchema(env)),
112
+ },
113
+ responses: {
114
+ "200": {
115
+ description: "SSH Host Certificate issued successfully",
116
+ ...certificateResponse,
117
+ },
118
+ ...UnauthorizedException.schema(),
119
+ ...ForbiddenException.schema(),
120
+ ...InputValidationException.schema(),
121
+ ...UnprocessableEntityException.schema(),
122
+ ...InternalServerErrorException.schema(),
123
+ }
124
+ });
125
+ export const renewHostCertificateEndpointBodySchema = (env) => (z.object({
126
+ certificate: openapiStringByte
127
+ .transform(transformCertificate)
128
+ .meta({ description: "SSH certificate to renew, which must have the serial number in the path" }),
129
+ lifetime: z.int()
130
+ .min(seconds("24 hours"))
131
+ .max(seconds(env.SSH_HOST_CERTIFICATE_LIFETIME))
132
+ .default(seconds(env.SSH_HOST_CERTIFICATE_LIFETIME))
133
+ .meta({ description: "Lifetime of renewed Host SSH certificate" }),
134
+ })
135
+ .superRefine((val, ctx) => refineRenewableCertificate(env, isRevoked, val, ctx))
136
+ .openapi("Host Certificate Renewal Request"));
137
+ export const createRenewHostCertificateEndpointSchema = (env) => ({
138
+ tags: [certificateTypeTag(CertificateType.Host)],
139
+ operationId: "renew_host_certificate",
140
+ summary: "Renew a host certificate",
141
+ description: `Issues a new SSH host certificate for the same public key and principals as an existing, unexpired and unrevoked host certificate issued by this CA. No OIDC authentication is required; instead the \`${ProofHeader}\` header must contain a proof of possession of the private key for the existing certificate made for this request. The lifetime of the renewed certificate is no longer than that of the existing certificate.`,
142
+ request: {
143
+ params: serialParamSchema,
144
+ headers: createProofHeaderSchema(env),
145
+ body: contentJson(renewHostCertificateEndpointBodySchema(env)),
146
+ },
147
+ responses: {
148
+ "200": {
149
+ description: "SSH Host Certificate renewed successfully",
150
+ ...certificateResponse,
151
+ },
152
+ ...UnauthorizedException.schema(),
153
+ ...ForbiddenException.schema(),
154
+ ...InputValidationException.schema(),
155
+ ...UnprocessableEntityException.schema(),
156
+ ...InternalServerErrorException.schema(),
157
+ }
158
+ });
159
+ export const createRevokeCertificateEndpointSchema = (env, certificateType) => ({
160
+ tags: [certificateTypeTag(certificateType)],
161
+ operationId: `revoke_${certificateTypeName(certificateType)}_certificate`,
162
+ summary: `Revoke a ${certificateTypeName(certificateType)} certificate`,
163
+ description: `Revokes a ${certificateTypeName(certificateType)} certificate issued by this CA and adds it to the ${certificateTypeName(certificateType)} Key Revocation List. The \`${ProofHeader}\` header must contain a proof of possession of the private key for the public key that the certificate was issued for, made for this request. A certificate issued for a different public key is reported as not found.`,
164
+ request: {
165
+ params: serialParamSchema,
166
+ headers: createProofHeaderSchema(env),
167
+ },
168
+ responses: {
169
+ "200": {
170
+ description: `SSH ${certificateTypeName(certificateType)} certificate revoked successfully`,
171
+ ...contentJson(z.object({
172
+ revoked_at: z.iso.datetime(),
173
+ })
174
+ .openapi("Revocation Response")),
175
+ },
176
+ ...InputValidationException.schema(),
177
+ ...NotFoundException.schema(),
178
+ ...ConflictException.schema(),
179
+ ...InternalServerErrorException.schema(),
180
+ }
181
+ });
182
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/v4/schema.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import type { Certificate, Identity, Key, PrivateKey } from "sshpk";
1
+ import type { PrivateKey, PublicKey } from "./ssh/keys.js";
2
2
  import type { SshCaBindings } from "./types.js";
3
+ import { type SshCertificate } from "./ssh/certificate.js";
3
4
  export type CreateCertificateOptions = {
4
5
  lifetime?: number;
5
6
  principals?: string[];
@@ -16,16 +17,18 @@ export type GenerateCertificateOptions = {
16
17
  */
17
18
  serial?: bigint;
18
19
  } & CreateCertificateOptions;
19
- export declare const generateCertificate: (env: SshCaBindings, email: string, key: PrivateKey, public_key: Key, options?: GenerateCertificateOptions) => Certificate;
20
- export declare function createSignedCertificate(env: SshCaBindings, email: string, public_key: Key, options?: CreateCertificateOptions): Promise<Certificate>;
20
+ export declare const generateCertificate: (env: SshCaBindings, email: string, key: PrivateKey, public_key: PublicKey, options?: GenerateCertificateOptions) => Promise<SshCertificate>;
21
+ export declare function createSignedCertificate(env: SshCaBindings, email: string, public_key: PublicKey, options?: CreateCertificateOptions): Promise<SshCertificate>;
21
22
  export type CreateHostCertificateOptions = {
22
23
  lifetime?: number;
23
24
  principals?: string[];
24
- subjects?: Identity[];
25
25
  };
26
26
  export declare class BadIssuerError extends Error {
27
27
  constructor(message: string);
28
28
  }
29
- export declare function createSignedHostCertificate(env: SshCaBindings, public_key: Key, options?: CreateHostCertificateOptions): Promise<Certificate>;
30
- export declare const generateSerial: (serial?: bigint) => Buffer<ArrayBuffer>;
29
+ export declare function createSignedHostCertificate(env: SshCaBindings, public_key: PublicKey, options?: CreateHostCertificateOptions): Promise<SshCertificate>;
30
+ /**
31
+ * Returns a random certificate serial number
32
+ */
33
+ export declare const generateSerial: () => bigint;
31
34
  //# sourceMappingURL=certificate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../src/certificate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAO/C,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB,CAAA;AAQD,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,YAAY,OAAO,EAAE,MAAM,EAM1B;CACD;AAED,MAAM,MAAM,0BAA0B,GAAG;IACxC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,wBAAwB,CAAA;AAE5B,eAAO,MAAM,mBAAmB,QAAS,aAAa,SAAS,MAAM,OAAO,UAAU,cAAc,GAAG,YAAY,0BAA0B,KAAG,WAkG/I,CAAA;AAED,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,WAAW,CAAC,CAa1J;AAED,MAAM,MAAM,4BAA4B,GAAG;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACrB,CAAA;AAED,qBAAa,cAAe,SAAQ,KAAK;IACxC,YAAY,OAAO,EAAE,MAAM,EAK1B;CACD;AAED,wBAAsB,2BAA2B,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,WAAW,CAAC,CAsCnJ;AAED,eAAO,MAAM,cAAc,YAAa,MAAM,KAAG,MAAM,CAAC,WAAW,CAUlE,CAAA"}
1
+ {"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../src/certificate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAGnG,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB,CAAA;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,YAAY,OAAO,EAAE,MAAM,EAM1B;CACD;AAED,MAAM,MAAM,0BAA0B,GAAG;IACxC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,wBAAwB,CAAA;AAqB5B,eAAO,MAAM,mBAAmB,QAAe,aAAa,SAAS,MAAM,OAAO,UAAU,cAAc,SAAS,YAAY,0BAA0B,KAAG,OAAO,CAAC,cAAc,CAsDjL,CAAA;AAED,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAanK;AAED,MAAM,MAAM,4BAA4B,GAAG;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB,CAAA;AAED,qBAAa,cAAe,SAAQ,KAAK;IACxC,YAAY,OAAO,EAAE,MAAM,EAK1B;CACD;AAED,wBAAsB,2BAA2B,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB5J;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,QAAO,MAEjC,CAAA"}
@@ -1,13 +1,7 @@
1
1
  import { seconds } from "itty-time";
2
- import sshpk from "sshpk";
3
2
  import { getPrivateKey, split } from "./utils.js";
4
- // sshpk is CommonJS and Node's ESM loader cannot detect most of its named exports, so values are taken from the default import
5
- const { createCertificate, identityForHost, identityForUser, identityFromDN } = sshpk;
6
- /* const DefaultCreateCertificateOptions: CreateCertificateOptions = {
7
- lifetime: seconds(env.SSH_CERTIFICATE_LIFETIME),
8
- principals: [],
9
- extensions: sshCertificateExtensions
10
- } */
3
+ import { signCertificate } from "./ssh/certificate.js";
4
+ import { createSigner } from "./ssh/signer.js";
11
5
  export class CertificateError extends Error {
12
6
  constructor(message) {
13
7
  super(message);
@@ -16,17 +10,32 @@ export class CertificateError extends Error {
16
10
  Object.setPrototypeOf(this, CertificateError.prototype);
17
11
  }
18
12
  }
19
- export const generateCertificate = (env, email, key, public_key, options) => {
20
- let { lifetime } = options ?? {};
21
- const { principals, extensions, serial } = options ?? {};
22
- // add identities
13
+ /**
14
+ * Signs a certificate with the CA key
15
+ *
16
+ * @throws {@link CertificateError} when the certificate has no principals,
17
+ * as OpenSSH accepts a certificate without principals for any user or host
18
+ */
19
+ const sign = async (key, fields, lifetime) => {
20
+ if (fields.principals.length === 0) {
21
+ throw new CertificateError("a certificate must have at least one principal");
22
+ }
23
+ // the validity is to the second, so starts at the current second rather
24
+ // than in the future
25
+ const validAfter = new Date(Math.floor(Date.now() / 1000) * 1000);
26
+ const validBefore = new Date(validAfter.getTime() + lifetime * 1000);
27
+ return await signCertificate({ ...fields, validAfter, validBefore }, await createSigner(key));
28
+ };
29
+ export const generateCertificate = async (env, email, key, public_key, options) => {
30
+ const { lifetime, principals, extensions, serial } = options ?? {};
31
+ // add principals
23
32
  const includeSelf = env.SSH_CERTIFICATE_INCLUDE_SELF === undefined
24
33
  ? false
25
34
  : typeof env.SSH_CERTIFICATE_INCLUDE_SELF === "string"
26
35
  ? env.SSH_CERTIFICATE_INCLUDE_SELF === "true"
27
36
  : env.SSH_CERTIFICATE_INCLUDE_SELF;
28
- const identity = includeSelf
29
- ? [identityForUser(email.split("@")[0])]
37
+ const certificatePrincipals = includeSelf
38
+ ? [email.split("@")[0]]
30
39
  : [];
31
40
  // add email?
32
41
  const includeSelfEmail = env.SSH_CERTIFICATE_INCLUDE_SELF_EMAIL === undefined
@@ -35,69 +44,34 @@ export const generateCertificate = (env, email, key, public_key, options) => {
35
44
  ? env.SSH_CERTIFICATE_INCLUDE_SELF_EMAIL === "true"
36
45
  : env.SSH_CERTIFICATE_INCLUDE_SELF_EMAIL;
37
46
  if (includeSelfEmail) {
38
- identity.push(identityForUser(email));
47
+ certificatePrincipals.push(email);
39
48
  }
40
49
  if (principals !== undefined) {
41
- for (const p of principals) {
42
- identity.push(identityForUser(p));
43
- }
44
- }
45
- for (const p of split(env.SSH_CERTIFICATE_PRINCIPALS)) {
46
- identity.push(identityForUser(p));
47
- }
48
- // lifetime is the smaller of what was provided in the options or the default
49
- lifetime = lifetime !== undefined
50
- ? Math.min(lifetime, seconds(env.SSH_CERTIFICATE_LIFETIME))
51
- : seconds(env.SSH_CERTIFICATE_LIFETIME);
52
- // generate value for serial of certificate
53
- const s = generateSerial(serial);
54
- // set issuer of certificate based on ISSUER_DN
55
- const issuer = identityFromDN(env.ISSUER_DN);
56
- // create certificate
57
- const certificate = createCertificate(identity, public_key, issuer, key, { lifetime: lifetime, serial: s });
58
- // ensure openssh info is included in certificate (should not occur)
59
- if (certificate.signatures.openssh === undefined) {
60
- throw new CertificateError("missing openssh information in certificate");
50
+ certificatePrincipals.push(...principals);
61
51
  }
52
+ certificatePrincipals.push(...split(env.SSH_CERTIFICATE_PRINCIPALS));
62
53
  const sshCertificateExtensions = split(env.SSH_CERTIFICATE_EXTENSIONS);
63
- // add usage extensions
64
- const sshextensions = [];
54
+ // if extensions are provided in request, ensure they do not include extra extensions beyond the defaults
65
55
  if (extensions !== undefined) {
66
- // if extensions are provided in request, ensure they do not include extra extensions beyond the defaults
67
56
  for (const ext of extensions) {
68
57
  if (!sshCertificateExtensions.includes(ext)) {
69
58
  throw new CertificateError(`${ext} is not allowed`);
70
59
  }
71
- // add to list of allowed extensions
72
- sshextensions.push({
73
- critical: false,
74
- name: ext,
75
- data: Buffer.alloc(0)
76
- });
77
60
  }
78
61
  }
79
- else {
62
+ // lifetime is the smaller of what was provided in the options or the default
63
+ const certificateLifetime = lifetime !== undefined
64
+ ? Math.min(lifetime, seconds(env.SSH_CERTIFICATE_LIFETIME))
65
+ : seconds(env.SSH_CERTIFICATE_LIFETIME);
66
+ return await sign(key, {
67
+ kind: "user",
68
+ publicKey: public_key,
69
+ serial: serial ?? generateSerial(),
70
+ keyId: email,
71
+ principals: certificatePrincipals,
80
72
  // use defaults if not provided
81
- sshCertificateExtensions.forEach((ext) => {
82
- sshextensions.push({
83
- critical: false,
84
- name: ext,
85
- data: Buffer.alloc(0)
86
- });
87
- });
88
- }
89
- // add info to certificate
90
- certificate.signatures = {
91
- openssh: {
92
- nonce: certificate.signatures.openssh.nonce,
93
- keyId: email,
94
- signature: certificate.signatures.openssh.signature,
95
- exts: sshextensions
96
- }
97
- };
98
- // re-sign after changes
99
- certificate.signWith(key);
100
- return certificate;
73
+ extensions: extensions ?? sshCertificateExtensions,
74
+ }, certificateLifetime);
101
75
  };
102
76
  export async function createSignedCertificate(env, email, public_key, options) {
103
77
  // grab private key from secret store
@@ -109,7 +83,7 @@ export async function createSignedCertificate(env, email, public_key, options) {
109
83
  extensions: split(env.SSH_CERTIFICATE_EXTENSIONS)
110
84
  };
111
85
  }
112
- return generateCertificate(env, email, key, public_key, options);
86
+ return await generateCertificate(env, email, key, public_key, options);
113
87
  }
114
88
  export class BadIssuerError extends Error {
115
89
  constructor(message) {
@@ -119,44 +93,26 @@ export class BadIssuerError extends Error {
119
93
  }
120
94
  }
121
95
  export async function createSignedHostCertificate(env, public_key, options) {
122
- if (options === undefined) {
123
- options = {
124
- lifetime: seconds(env.SSH_HOST_CERTIFICATE_LIFETIME),
125
- principals: []
126
- };
127
- }
128
- // generate list of identities for host key
129
- const identity = [];
130
- if (options.principals !== undefined) {
131
- for (const p of options.principals) {
132
- identity.push(identityForHost(p));
133
- }
134
- }
135
- // add any already provided subjects (for renewals only)
136
- if (options.subjects !== undefined) {
137
- identity.push(...options.subjects);
138
- }
96
+ const principals = options?.principals ?? [];
139
97
  // grab private key from secret store
140
98
  const key = await getPrivateKey(env);
141
99
  // lifetime is the smaller of what was provided in the options or the default
142
- const lifetime = options.lifetime !== undefined
100
+ const lifetime = options?.lifetime !== undefined
143
101
  ? Math.min(options.lifetime, seconds(env.SSH_HOST_CERTIFICATE_LIFETIME))
144
102
  : seconds(env.SSH_HOST_CERTIFICATE_LIFETIME);
145
- // generate value for serial of certificate
146
- const serial = generateSerial();
147
- // set issuer of certificate based on ISSUER_DN
148
- const issuer = identityFromDN(env.ISSUER_DN);
149
- // create certificate
150
- const certificate = createCertificate(identity, public_key, issuer, key, { lifetime: lifetime, serial: serial });
151
- return certificate;
103
+ return await sign(key, {
104
+ kind: "host",
105
+ publicKey: public_key,
106
+ serial: generateSerial(),
107
+ keyId: `host_${principals[0]}`,
108
+ principals,
109
+ extensions: [],
110
+ }, lifetime);
152
111
  }
153
- export const generateSerial = (serial) => {
154
- if (serial !== undefined) {
155
- const s = Buffer.alloc(8);
156
- s.writeBigUInt64BE(serial);
157
- return s;
158
- }
159
- const randomBytes = crypto.getRandomValues(new Uint8Array(8));
160
- return Buffer.from(randomBytes);
112
+ /**
113
+ * Returns a random certificate serial number
114
+ */
115
+ export const generateSerial = () => {
116
+ return new DataView(crypto.getRandomValues(new Uint8Array(8)).buffer).getBigUint64(0);
161
117
  };
162
118
  //# sourceMappingURL=certificate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"certificate.js","sourceRoot":"","sources":["../src/certificate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"certificate.js","sourceRoot":"","sources":["../src/certificate.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,28 @@
1
- import type { Certificate } from "sshpk";
1
+ import { PublicKey } from "../ssh/keys.js";
2
+ import type { SshCertificate } from "../ssh/certificate.js";
2
3
  import type { SshCaBindings } from "../types.js";
3
4
  import { CertificateType, type CertificateRecord } from "./types.js";
4
5
  export declare const dbCleanup: (env: SshCaBindings) => Promise<void>;
5
- export declare const recordCertificate: (env: SshCaBindings, certificate: Certificate, keyid: string, certificateType?: CertificateType) => Promise<void>;
6
+ export declare const recordCertificate: (env: SshCaBindings, certificate: SshCertificate, keyid: string, certificateType?: CertificateType) => Promise<void>;
6
7
  export declare const isRevoked: (env: SshCaBindings, serial: bigint) => Promise<boolean>;
7
8
  export declare enum RevocationStatus {
8
9
  NotFound = 0,
9
10
  Revoked = 1,
10
- Revokable = 2,
11
- Unrevokable = 3
11
+ Revokable = 2
12
12
  }
13
- export declare const revocationStatus: (env: SshCaBindings, serial: bigint, publickey: string, certificateType: CertificateType) => Promise<RevocationStatus>;
13
+ /**
14
+ * Returns whether a certificate can be revoked by the holder of a public key.
15
+ *
16
+ * The public key is compared by fingerprint, so a key with a different
17
+ * comment to the one the certificate was issued for still matches.
18
+ *
19
+ * @param env The CA bindings
20
+ * @param serial The serial of the certificate
21
+ * @param publicKey The public key the requester has proven possession of
22
+ * @param certificateType The type of certificate
23
+ * @returns The revocation status of the certificate
24
+ */
25
+ export declare const revocationStatus: (env: SshCaBindings, serial: bigint, publicKey: PublicKey, certificateType: CertificateType) => Promise<RevocationStatus>;
14
26
  export declare const getRevocationList: (env: SshCaBindings, certificateType: CertificateType) => Promise<string[]>;
15
27
  type RevokedCertificate = Pick<CertificateRecord, "revoked_at">;
16
28
  export declare const revokeCertificate: (env: SshCaBindings, serial: bigint) => Promise<RevokedCertificate[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,OAAO,CAAA;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKhD,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAuC,MAAM,YAAY,CAAA;AAmBzG,eAAO,MAAM,SAAS,QAAe,aAAa,kBAsBjD,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAe,aAAa,eAAe,WAAW,SAAS,MAAM,oBAAmB,eAAe,kBA2BpI,CAAA;AAED,eAAO,MAAM,SAAS,QAAe,aAAa,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAYnF,CAAA;AAED,oBAAY,gBAAgB;IAC3B,QAAQ,IAAA;IACR,OAAO,IAAA;IACP,SAAS,IAAA;IACT,WAAW,IAAA;CACX;AAED,eAAO,MAAM,gBAAgB,QAAe,aAAa,UAAU,MAAM,aAAa,MAAM,mBAAmB,eAAe,KAAG,OAAO,CAAC,gBAAgB,CAwBxJ,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAe,aAAa,mBAAmB,eAAe,KAAG,OAAO,CAAC,MAAM,EAAE,CAsB9G,CAAA;AAED,KAAK,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAA;AAE/D,eAAO,MAAM,iBAAiB,QAAe,aAAa,UAAU,MAAM,KAAG,OAAO,CAAC,kBAAkB,EAAE,CAuBxG,CAAA;AAUD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,QAAe,aAAa,mBAAmB,eAAe,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CA4CxH,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKhD,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAuC,MAAM,YAAY,CAAA;AAmBzG,eAAO,MAAM,SAAS,QAAe,aAAa,kBAsBjD,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAe,aAAa,eAAe,cAAc,SAAS,MAAM,oBAAmB,eAAe,kBAsBvI,CAAA;AAED,eAAO,MAAM,SAAS,QAAe,aAAa,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAYnF,CAAA;AAED,oBAAY,gBAAgB;IAC3B,QAAQ,IAAA;IACR,OAAO,IAAA;IACP,SAAS,IAAA;CACT;AAYD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,QAAe,aAAa,UAAU,MAAM,aAAa,SAAS,mBAAmB,eAAe,KAAG,OAAO,CAAC,gBAAgB,CAsB3J,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAe,aAAa,mBAAmB,eAAe,KAAG,OAAO,CAAC,MAAM,EAAE,CAsB9G,CAAA;AAED,KAAK,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAA;AAE/D,eAAO,MAAM,iBAAiB,QAAe,aAAa,UAAU,MAAM,KAAG,OAAO,CAAC,kBAAkB,EAAE,CAuBxG,CAAA;AAUD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,QAAe,aAAa,mBAAmB,eAAe,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CA4CxH,CAAA"}
package/dist/db/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { PublicKey } from "../ssh/keys.js";
1
2
  import { logger } from "../logger.js";
2
3
  import { D1QB } from "workers-qb";
3
4
  import { z } from "zod";
@@ -40,26 +41,21 @@ export const dbCleanup = async (env) => {
40
41
  }
41
42
  };
42
43
  export const recordCertificate = async (env, certificate, keyid, certificateType = CertificateType.User) => {
43
- const serial = certificate.serial.readBigUInt64BE(0);
44
- const subjects = certificate.subjects.map((v) => {
45
- return v.toString();
46
- }).join(",");
47
- const extensions = certificate.getExtensions().map((v) => {
48
- // @ts-expect-error: the name property does exist
49
- return v.name;
50
- }).join(",");
44
+ // principals are recorded as distinguished names, as they were when
45
+ // certificates were created with sshpk
46
+ const principals = certificate.principals.map((p) => `${certificate.kind === "host" ? "CN" : "UID"}=${p}`).join(",");
51
47
  const qb = await connect(env);
52
48
  const res = await qb.insert({
53
49
  tableName: "certificates",
54
50
  data: {
55
- serial: `${serial}`,
51
+ serial: `${certificate.serial}`,
56
52
  key_id: keyid,
57
- principals: subjects,
58
- extensions: extensions,
59
- valid_after: certificate.validFrom.toISOString(),
60
- valid_before: certificate.validUntil.toISOString(),
53
+ principals: principals,
54
+ extensions: certificate.extensions.join(","),
55
+ valid_after: certificate.validAfter.toISOString(),
56
+ valid_before: certificate.validBefore.toISOString(),
61
57
  certificate_type: certificateType,
62
- public_key: certificate.subjectKey.toString("ssh")
58
+ public_key: certificate.publicKey.toString()
63
59
  },
64
60
  }).execute();
65
61
  if (!res.success) {
@@ -83,28 +79,48 @@ export var RevocationStatus;
83
79
  RevocationStatus[RevocationStatus["NotFound"] = 0] = "NotFound";
84
80
  RevocationStatus[RevocationStatus["Revoked"] = 1] = "Revoked";
85
81
  RevocationStatus[RevocationStatus["Revokable"] = 2] = "Revokable";
86
- RevocationStatus[RevocationStatus["Unrevokable"] = 3] = "Unrevokable";
87
82
  })(RevocationStatus || (RevocationStatus = {}));
88
- export const revocationStatus = async (env, serial, publickey, certificateType) => {
83
+ // whether a public key recorded in OpenSSH format is the same as another key,
84
+ // treating a recorded key that cannot be parsed as a different key
85
+ const isKey = (recorded, publicKey) => {
86
+ try {
87
+ return PublicKey.parse(recorded).equals(publicKey);
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ };
93
+ /**
94
+ * Returns whether a certificate can be revoked by the holder of a public key.
95
+ *
96
+ * The public key is compared by fingerprint, so a key with a different
97
+ * comment to the one the certificate was issued for still matches.
98
+ *
99
+ * @param env The CA bindings
100
+ * @param serial The serial of the certificate
101
+ * @param publicKey The public key the requester has proven possession of
102
+ * @param certificateType The type of certificate
103
+ * @returns The revocation status of the certificate
104
+ */
105
+ export const revocationStatus = async (env, serial, publicKey, certificateType) => {
89
106
  const qb = await connect(env);
90
107
  const res = await qb.fetchOne({
91
108
  tableName: "certificates",
92
109
  fields: ["public_key", "revoked_at"],
93
110
  where: {
94
- conditions: "serial = ? AND public_key = ? AND certificate_type = ?",
95
- params: [`${serial}`, publickey, certificateType]
111
+ conditions: "serial = ? AND certificate_type = ?",
112
+ params: [`${serial}`, certificateType]
96
113
  }
97
114
  }).execute();
98
- if (res.results === undefined) {
115
+ // a certificate issued for another key is reported as not found, so
116
+ // requests cannot be used to discover which serials exist
117
+ if (res.results?.public_key == null || !isKey(res.results.public_key, publicKey)) {
99
118
  return RevocationStatus.NotFound;
100
119
  }
101
120
  if (res.results.revoked_at !== null) {
102
121
  return RevocationStatus.Revoked;
103
122
  }
104
- if (publickey === res.results.public_key) {
105
- return RevocationStatus.Revokable;
106
- }
107
- return RevocationStatus.Unrevokable;
123
+ return RevocationStatus.Revokable;
108
124
  };
109
125
  export const getRevocationList = async (env, certificateType) => {
110
126
  const qb = await connect(env);