@alchemy.run/acme 0.0.0 → 2.0.0-beta.85
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/LICENSE +201 -0
- package/README.md +48 -4
- package/lib/Account.d.ts +121 -0
- package/lib/Account.d.ts.map +1 -0
- package/lib/Account.js +163 -0
- package/lib/Account.js.map +1 -0
- package/lib/Certificate.d.ts +138 -0
- package/lib/Certificate.d.ts.map +1 -0
- package/lib/Certificate.js +157 -0
- package/lib/Certificate.js.map +1 -0
- package/lib/CertificateAuthority.d.ts +25 -0
- package/lib/CertificateAuthority.d.ts.map +1 -0
- package/lib/CertificateAuthority.js +22 -0
- package/lib/CertificateAuthority.js.map +1 -0
- package/lib/Client.d.ts +89 -0
- package/lib/Client.d.ts.map +1 -0
- package/lib/Client.js +237 -0
- package/lib/Client.js.map +1 -0
- package/lib/Der.d.ts +43 -0
- package/lib/Der.d.ts.map +1 -0
- package/lib/Der.js +138 -0
- package/lib/Der.js.map +1 -0
- package/lib/Dns.d.ts +59 -0
- package/lib/Dns.d.ts.map +1 -0
- package/lib/Dns.js +104 -0
- package/lib/Dns.js.map +1 -0
- package/lib/DnsSolver.d.ts +60 -0
- package/lib/DnsSolver.d.ts.map +1 -0
- package/lib/DnsSolver.js +48 -0
- package/lib/DnsSolver.js.map +1 -0
- package/lib/Errors.d.ts +93 -0
- package/lib/Errors.d.ts.map +1 -0
- package/lib/Errors.js +51 -0
- package/lib/Errors.js.map +1 -0
- package/lib/IssueCertificate.d.ts +76 -0
- package/lib/IssueCertificate.d.ts.map +1 -0
- package/lib/IssueCertificate.js +3 -0
- package/lib/IssueCertificate.js.map +1 -0
- package/lib/IssueCertificateHttp.d.ts +24 -0
- package/lib/IssueCertificateHttp.d.ts.map +1 -0
- package/lib/IssueCertificateHttp.js +56 -0
- package/lib/IssueCertificateHttp.js.map +1 -0
- package/lib/Pki.d.ts +56 -0
- package/lib/Pki.d.ts.map +1 -0
- package/lib/Pki.js +182 -0
- package/lib/Pki.js.map +1 -0
- package/lib/Providers.d.ts +30 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +31 -0
- package/lib/Providers.js.map +1 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/package.json +47 -3
- package/src/Account.ts +254 -0
- package/src/Certificate.ts +272 -0
- package/src/CertificateAuthority.ts +41 -0
- package/src/Client.ts +405 -0
- package/src/Der.ts +157 -0
- package/src/Dns.ts +227 -0
- package/src/DnsSolver.ts +109 -0
- package/src/Errors.ts +90 -0
- package/src/IssueCertificate.ts +89 -0
- package/src/IssueCertificateHttp.ts +67 -0
- package/src/Pki.ts +275 -0
- package/src/Providers.ts +39 -0
- package/src/index.ts +11 -0
package/lib/Pki.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Jose } from "@distilled.cloud/acme";
|
|
2
|
+
/**
|
|
3
|
+
* Keys, PKCS#10 certificate requests and X.509 parsing for the ACME flow.
|
|
4
|
+
* WebCrypto plus the tiny DER codec in `Der.ts` — no native code, so the
|
|
5
|
+
* same code runs at deploy time and inside a Worker or Fly Service.
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Redacted from "effect/Redacted";
|
|
9
|
+
import * as Der from "./Der.js";
|
|
10
|
+
import { PkiError } from "./Errors.js";
|
|
11
|
+
const OID = {
|
|
12
|
+
commonName: "2.5.4.3",
|
|
13
|
+
organization: "2.5.4.10",
|
|
14
|
+
extensionRequest: "1.2.840.113549.1.9.14",
|
|
15
|
+
subjectAltName: "2.5.29.17",
|
|
16
|
+
ecdsaWithSha256: "1.2.840.10045.4.3.2",
|
|
17
|
+
sha256WithRsa: "1.2.840.113549.1.1.11",
|
|
18
|
+
};
|
|
19
|
+
const keyGenParams = (alg) => alg === "RS256"
|
|
20
|
+
? {
|
|
21
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
22
|
+
modulusLength: 2048,
|
|
23
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
24
|
+
hash: "SHA-256",
|
|
25
|
+
}
|
|
26
|
+
: { name: "ECDSA", namedCurve: "P-256" };
|
|
27
|
+
const importParams = (alg) => alg === "RS256"
|
|
28
|
+
? { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }
|
|
29
|
+
: { name: "ECDSA", namedCurve: "P-256" };
|
|
30
|
+
const signParams = (alg) => alg === "RS256" ? { name: "RSASSA-PKCS1-v1_5" } : { name: "ECDSA", hash: "SHA-256" };
|
|
31
|
+
const toPem = (label, der) => {
|
|
32
|
+
const b64 = btoa(String.fromCharCode(...der));
|
|
33
|
+
const lines = b64.match(/.{1,64}/g) ?? [];
|
|
34
|
+
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
|
35
|
+
};
|
|
36
|
+
/** The DER bytes of the first (or `index`-th) PEM block in `pem`. */
|
|
37
|
+
export const fromPem = (pem, index = 0) => {
|
|
38
|
+
const blocks = pem.match(/-----BEGIN [^-]+-----[\s\S]*?-----END [^-]+-----/g);
|
|
39
|
+
const block = blocks?.[index];
|
|
40
|
+
if (block === undefined) {
|
|
41
|
+
throw new Error(`PEM block ${index} not found`);
|
|
42
|
+
}
|
|
43
|
+
const body = block
|
|
44
|
+
.replace(/-----BEGIN [^-]+-----/, "")
|
|
45
|
+
.replace(/-----END [^-]+-----/, "")
|
|
46
|
+
.replace(/\s+/g, "");
|
|
47
|
+
const binary = atob(body);
|
|
48
|
+
const out = new Uint8Array(binary.length);
|
|
49
|
+
for (let i = 0; i < binary.length; i++)
|
|
50
|
+
out[i] = binary.charCodeAt(i);
|
|
51
|
+
return out;
|
|
52
|
+
};
|
|
53
|
+
/** Every PEM block in a chain, leaf first. */
|
|
54
|
+
export const splitPemChain = (pem) => pem.match(/-----BEGIN [^-]+-----[\s\S]*?-----END [^-]+-----/g) ?? [];
|
|
55
|
+
const tryPromise = (message, run) => Effect.tryPromise({
|
|
56
|
+
try: run,
|
|
57
|
+
catch: (cause) => new PkiError({ message, cause }),
|
|
58
|
+
});
|
|
59
|
+
/** Generate a certificate key pair (ES256 P-256 by default). */
|
|
60
|
+
export const generateKey = (algorithm = "ES256") => tryPromise("Generating the certificate key failed.", async () => {
|
|
61
|
+
const pair = await crypto.subtle.generateKey(keyGenParams(algorithm), true, ["sign", "verify"]);
|
|
62
|
+
const pkcs8 = new Uint8Array(await crypto.subtle.exportKey("pkcs8", pair.privateKey));
|
|
63
|
+
const spki = new Uint8Array(await crypto.subtle.exportKey("spki", pair.publicKey));
|
|
64
|
+
return {
|
|
65
|
+
algorithm,
|
|
66
|
+
privateKeyPem: Redacted.make(toPem("PRIVATE KEY", pkcs8)),
|
|
67
|
+
privateKey: pair.privateKey,
|
|
68
|
+
spki,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
const algorithmOfPkcs8 = (der) => {
|
|
72
|
+
// PrivateKeyInfo ::= SEQUENCE { version, AlgorithmIdentifier, ... }
|
|
73
|
+
const root = Der.read(der, 0);
|
|
74
|
+
const [, algorithm] = Der.children(root);
|
|
75
|
+
const [algOid] = Der.children(algorithm);
|
|
76
|
+
return Der.decodeOid(algOid) === "1.2.840.113549.1.1.1" ? "RS256" : "ES256";
|
|
77
|
+
};
|
|
78
|
+
/** Import a PKCS#8 PEM private key (ES256 or RS256, detected from the DER). */
|
|
79
|
+
export const importPrivateKey = (pem) => tryPromise("Importing the certificate key failed.", async () => {
|
|
80
|
+
const text = Redacted.isRedacted(pem) ? Redacted.value(pem) : pem;
|
|
81
|
+
const der = fromPem(text);
|
|
82
|
+
const algorithm = algorithmOfPkcs8(der);
|
|
83
|
+
const key = await crypto.subtle.importKey("pkcs8", der, importParams(algorithm), true, ["sign"]);
|
|
84
|
+
return { key, algorithm };
|
|
85
|
+
});
|
|
86
|
+
/** The private JWK of a PKCS#8 PEM key (for revocation signed by the certificate key). */
|
|
87
|
+
export const privateKeyToJwk = (pem) => importPrivateKey(pem).pipe(Effect.flatMap(({ key }) => tryPromise("Exporting the certificate key as a JWK failed.", async () => Redacted.make(JSON.stringify(await crypto.subtle.exportKey("jwk", key))))));
|
|
88
|
+
/** Raw `r || s` ECDSA signature → DER `SEQUENCE { r INTEGER, s INTEGER }`. */
|
|
89
|
+
const ecdsaToDer = (raw) => {
|
|
90
|
+
const half = raw.length / 2;
|
|
91
|
+
return Der.sequence(Der.integer(raw.subarray(0, half)), Der.integer(raw.subarray(half)));
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* A PKCS#10 certificate request for `identifiers` signed by `key`: the
|
|
95
|
+
* first identifier is the subject CN, all of them are `subjectAltName`
|
|
96
|
+
* dNSName entries. Returned as DER.
|
|
97
|
+
*/
|
|
98
|
+
export const createCsr = (options) => tryPromise("Encoding the certificate request failed.", async () => {
|
|
99
|
+
const { key, identifiers } = options;
|
|
100
|
+
if (identifiers.length === 0) {
|
|
101
|
+
throw new Error("a certificate needs at least one identifier");
|
|
102
|
+
}
|
|
103
|
+
const subject = Der.sequence(Der.set(Der.sequence(Der.oid(OID.commonName), Der.utf8String(identifiers[0]))));
|
|
104
|
+
const sanExtension = Der.sequence(Der.oid(OID.subjectAltName), Der.octetString(Der.sequence(...identifiers.map((name) => Der.contextPrimitive(2, new TextEncoder().encode(name))))));
|
|
105
|
+
const attributes = Der.contextTag(0, Der.sequence(Der.oid(OID.extensionRequest), Der.set(Der.sequence(sanExtension))));
|
|
106
|
+
const info = Der.sequence(Der.integer(0), subject, key.spki, attributes);
|
|
107
|
+
const rawSignature = new Uint8Array(await crypto.subtle.sign(signParams(key.algorithm), key.privateKey, info));
|
|
108
|
+
const signature = key.algorithm === "ES256" ? ecdsaToDer(rawSignature) : rawSignature;
|
|
109
|
+
const algorithm = key.algorithm === "ES256"
|
|
110
|
+
? Der.sequence(Der.oid(OID.ecdsaWithSha256))
|
|
111
|
+
: Der.sequence(Der.oid(OID.sha256WithRsa), Der.nullValue());
|
|
112
|
+
return Der.sequence(info, algorithm, Der.bitString(signature));
|
|
113
|
+
});
|
|
114
|
+
/** base64url of a DER CSR, as ACME's `finalize` expects. */
|
|
115
|
+
export const csrToBase64Url = (der) => Jose.base64url(der);
|
|
116
|
+
const decodeName = (name) => {
|
|
117
|
+
const parts = [];
|
|
118
|
+
for (const rdn of Der.children(name)) {
|
|
119
|
+
for (const attribute of Der.children(rdn)) {
|
|
120
|
+
const [typeNode, valueNode] = Der.children(attribute);
|
|
121
|
+
const type = Der.decodeOid(typeNode);
|
|
122
|
+
const value = new TextDecoder().decode(Der.content(valueNode));
|
|
123
|
+
const label = type === OID.commonName
|
|
124
|
+
? "CN"
|
|
125
|
+
: type === OID.organization
|
|
126
|
+
? "O"
|
|
127
|
+
: type === "2.5.4.6"
|
|
128
|
+
? "C"
|
|
129
|
+
: type;
|
|
130
|
+
parts.push(`${label}=${value}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// CN first for readability; the rest keep their order.
|
|
134
|
+
parts.sort((a, b) => (a.startsWith("CN=") ? -1 : b.startsWith("CN=") ? 1 : 0));
|
|
135
|
+
return parts.join(", ");
|
|
136
|
+
};
|
|
137
|
+
/** Parse the first certificate of a PEM (or a DER buffer). */
|
|
138
|
+
export const parseCertificate = (input) => Effect.try({
|
|
139
|
+
try: () => {
|
|
140
|
+
const der = typeof input === "string" ? fromPem(input) : input;
|
|
141
|
+
const certificate = Der.read(der, 0);
|
|
142
|
+
const [tbs] = Der.children(certificate);
|
|
143
|
+
const fields = Der.children(tbs);
|
|
144
|
+
let index = 0;
|
|
145
|
+
if (fields[0].tag === 0xa0)
|
|
146
|
+
index++; // [0] EXPLICIT version
|
|
147
|
+
const serial = Der.toHex(Der.content(fields[index++]));
|
|
148
|
+
index++; // signature AlgorithmIdentifier
|
|
149
|
+
const issuer = decodeName(fields[index++]);
|
|
150
|
+
const [notBeforeNode, notAfterNode] = Der.children(fields[index++]);
|
|
151
|
+
const subject = decodeName(fields[index++]);
|
|
152
|
+
index++; // subjectPublicKeyInfo
|
|
153
|
+
const dnsNames = [];
|
|
154
|
+
for (const field of fields.slice(index)) {
|
|
155
|
+
if (field.tag !== 0xa3)
|
|
156
|
+
continue; // [3] EXPLICIT extensions
|
|
157
|
+
const [extensions] = Der.children(field);
|
|
158
|
+
for (const extension of Der.children(extensions)) {
|
|
159
|
+
const members = Der.children(extension);
|
|
160
|
+
if (Der.decodeOid(members[0]) !== OID.subjectAltName)
|
|
161
|
+
continue;
|
|
162
|
+
const valueNode = members[members.length - 1]; // OCTET STRING (critical may precede)
|
|
163
|
+
const generalNames = Der.read(Der.content(valueNode), 0);
|
|
164
|
+
for (const generalName of Der.children(generalNames)) {
|
|
165
|
+
if (generalName.tag === 0x82) {
|
|
166
|
+
dnsNames.push(new TextDecoder().decode(Der.content(generalName)));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
serial: serial.replace(/^00/, "") || "0",
|
|
173
|
+
notBefore: Der.decodeTime(notBeforeNode),
|
|
174
|
+
notAfter: Der.decodeTime(notAfterNode),
|
|
175
|
+
issuer,
|
|
176
|
+
subject,
|
|
177
|
+
dnsNames,
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
catch: (cause) => new PkiError({ message: "Parsing the certificate failed.", cause }),
|
|
181
|
+
});
|
|
182
|
+
//# sourceMappingURL=Pki.js.map
|
package/lib/Pki.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pki.js","sourceRoot":"","sources":["../src/Pki.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as Provider from "alchemy/Provider";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
declare const Providers_base: Provider.ProviderCollection<Providers, "ACME">;
|
|
4
|
+
export declare class Providers extends Providers_base {
|
|
5
|
+
}
|
|
6
|
+
export type ProviderRequirements = Layer.Services<ReturnType<typeof providers>>;
|
|
7
|
+
/**
|
|
8
|
+
* Registers the ACME resource providers ({@link Account},
|
|
9
|
+
* {@link Certificate}) and the {@link IssueCertificateHttp} binding. DNS-01
|
|
10
|
+
* solvers come from the DNS provider, e.g. `Cloudflare.providers()` registers
|
|
11
|
+
* `Cloudflare.ACME.ZoneSolver`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* export default Alchemy.Stack(
|
|
16
|
+
* "Certs",
|
|
17
|
+
* {
|
|
18
|
+
* providers: Layer.mergeAll(ACME.providers(), Cloudflare.providers()),
|
|
19
|
+
* state: Alchemy.localState(),
|
|
20
|
+
* },
|
|
21
|
+
* Effect.gen(function* () {
|
|
22
|
+
* const wildcard = yield* Wildcard;
|
|
23
|
+
* return { notAfter: wildcard.notAfter };
|
|
24
|
+
* }),
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const providers: () => Layer.Layer<import("effect/http/HttpClient").HttpClient | import("./IssueCertificate.ts").IssueCertificate | Providers, never, never>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=Providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Providers.d.ts","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;AAKtC,qBAAa,SAAU,SAAQ,cAAgD;CAAG;AAElF,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,6IAMnB,CAAC"}
|
package/lib/Providers.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as Provider from "alchemy/Provider";
|
|
2
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import { Account, AccountProvider } from "./Account.js";
|
|
5
|
+
import { Certificate, CertificateProvider } from "./Certificate.js";
|
|
6
|
+
import { IssueCertificateHttp } from "./IssueCertificateHttp.js";
|
|
7
|
+
export class Providers extends Provider.ProviderCollection()("ACME") {
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Registers the ACME resource providers ({@link Account},
|
|
11
|
+
* {@link Certificate}) and the {@link IssueCertificateHttp} binding. DNS-01
|
|
12
|
+
* solvers come from the DNS provider, e.g. `Cloudflare.providers()` registers
|
|
13
|
+
* `Cloudflare.ACME.ZoneSolver`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* export default Alchemy.Stack(
|
|
18
|
+
* "Certs",
|
|
19
|
+
* {
|
|
20
|
+
* providers: Layer.mergeAll(ACME.providers(), Cloudflare.providers()),
|
|
21
|
+
* state: Alchemy.localState(),
|
|
22
|
+
* },
|
|
23
|
+
* Effect.gen(function* () {
|
|
24
|
+
* const wildcard = yield* Wildcard;
|
|
25
|
+
* return { notAfter: wildcard.notAfter };
|
|
26
|
+
* }),
|
|
27
|
+
* );
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const providers = () => Layer.effect(Providers, Provider.collection([Account, Certificate])).pipe(Layer.provide(Layer.mergeAll(AccountProvider(), CertificateProvider())), Layer.provideMerge(IssueCertificateHttp), Layer.provideMerge(FetchHttpClient.layer), Layer.orDie);
|
|
31
|
+
//# sourceMappingURL=Providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Providers.js","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,eAAe,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,OAAO,SAAU,SAAQ,QAAQ,CAAC,kBAAkB,EAAa,CAAC,MAAM,CAAC;CAAG;AAIlF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAC5B,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CACvE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC,EACvE,KAAK,CAAC,YAAY,CAAC,oBAAoB,CAAC,EACxC,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,EACzC,KAAK,CAAC,KAAK,CACZ,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./Account.ts";
|
|
2
|
+
export * from "./Certificate.ts";
|
|
3
|
+
export * from "./CertificateAuthority.ts";
|
|
4
|
+
export * from "./Client.ts";
|
|
5
|
+
export * from "./Dns.ts";
|
|
6
|
+
export * from "./DnsSolver.ts";
|
|
7
|
+
export * from "./Errors.ts";
|
|
8
|
+
export * from "./IssueCertificate.ts";
|
|
9
|
+
export * from "./IssueCertificateHttp.ts";
|
|
10
|
+
export * from "./Pki.ts";
|
|
11
|
+
export * from "./Providers.ts";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./Account.js";
|
|
2
|
+
export * from "./Certificate.js";
|
|
3
|
+
export * from "./CertificateAuthority.js";
|
|
4
|
+
export * from "./Client.js";
|
|
5
|
+
export * from "./Dns.js";
|
|
6
|
+
export * from "./DnsSolver.js";
|
|
7
|
+
export * from "./Errors.js";
|
|
8
|
+
export * from "./IssueCertificate.js";
|
|
9
|
+
export * from "./IssueCertificateHttp.js";
|
|
10
|
+
export * from "./Pki.js";
|
|
11
|
+
export * from "./Providers.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alchemy.run/acme",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "2.0.0-beta.85",
|
|
4
4
|
"description": "ACME (Let's Encrypt, ZeroSSL) certificates for Alchemy, with a Cloudflare DNS-01 solver.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acme",
|
|
@@ -10,12 +10,56 @@
|
|
|
10
10
|
"letsencrypt",
|
|
11
11
|
"tls"
|
|
12
12
|
],
|
|
13
|
+
"homepage": "https://alchemy.run",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/alchemy-run/alchemy/issues"
|
|
16
|
+
},
|
|
13
17
|
"license": "Apache-2.0",
|
|
14
18
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
15
|
-
"homepage": "https://alchemy.run",
|
|
16
19
|
"repository": {
|
|
17
20
|
"type": "git",
|
|
18
21
|
"url": "git+https://github.com/alchemy-run/alchemy.git",
|
|
19
22
|
"directory": "packages/acme"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib",
|
|
26
|
+
"src"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./lib/index.d.ts",
|
|
33
|
+
"bun": "./src/index.ts",
|
|
34
|
+
"worker": "./src/index.ts",
|
|
35
|
+
"default": "./lib/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./*": {
|
|
38
|
+
"types": "./lib/*.d.ts",
|
|
39
|
+
"bun": "./src/*.ts",
|
|
40
|
+
"worker": "./src/*.ts",
|
|
41
|
+
"default": "./lib/*.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@distilled.cloud/acme": "1.0.0-rc.18"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/bun": "1.4.3",
|
|
53
|
+
"alchemy": "2.0.0-beta.85",
|
|
54
|
+
"effect": "^4.1.0",
|
|
55
|
+
"typescript": "^7.0.2"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"alchemy": "2.0.0-beta.85",
|
|
59
|
+
"effect": "^4.1.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "pnpm -r --filter \"{.}...\" run build:package",
|
|
63
|
+
"build:package": "node scripts/build.ts"
|
|
20
64
|
}
|
|
21
|
-
}
|
|
65
|
+
}
|
package/src/Account.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import * as Acme from "@distilled.cloud/acme";
|
|
2
|
+
import * as acme from "@distilled.cloud/acme/acme";
|
|
3
|
+
import { isResolved } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import { Resource } from "alchemy/Resource";
|
|
6
|
+
import * as Data from "effect/Data";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Redacted from "effect/Redacted";
|
|
9
|
+
import type { CertificateAuthority } from "./CertificateAuthority.ts";
|
|
10
|
+
import { accountLayer, type AccountCredentials } from "./Client.ts";
|
|
11
|
+
import type { KeyAlgorithm } from "./Pki.ts";
|
|
12
|
+
import type { Providers } from "./Providers.ts";
|
|
13
|
+
|
|
14
|
+
export interface ExternalAccountBinding {
|
|
15
|
+
/** The key id the CA issued (ZeroSSL: `eab_kid`). */
|
|
16
|
+
keyId: string;
|
|
17
|
+
/** The base64url HMAC key the CA issued (ZeroSSL: `eab_hmac_key`). */
|
|
18
|
+
hmacKey: Redacted.Redacted<string> | string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AccountProps {
|
|
22
|
+
/**
|
|
23
|
+
* The certificate authority: a preset ({@link LetsEncrypt},
|
|
24
|
+
* {@link ZeroSSL}, …) or `{ directoryUrl }`. Changing it replaces the
|
|
25
|
+
* account.
|
|
26
|
+
*/
|
|
27
|
+
ca: CertificateAuthority;
|
|
28
|
+
/**
|
|
29
|
+
* Contact URLs (`mailto:…`) the CA notifies about expiry and incidents.
|
|
30
|
+
* Updated in place.
|
|
31
|
+
*/
|
|
32
|
+
contact?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* You must agree to the CA's terms of service. Explicit on purpose —
|
|
35
|
+
* never defaulted.
|
|
36
|
+
*/
|
|
37
|
+
termsOfServiceAgreed: true;
|
|
38
|
+
/**
|
|
39
|
+
* External Account Binding for CAs that require one (ZeroSSL, Google
|
|
40
|
+
* Trust Services). Changing the key id replaces the account.
|
|
41
|
+
*/
|
|
42
|
+
eab?: ExternalAccountBinding;
|
|
43
|
+
/**
|
|
44
|
+
* Account key algorithm. Changing it replaces the account.
|
|
45
|
+
* @default "ES256"
|
|
46
|
+
*/
|
|
47
|
+
keyAlgorithm?: KeyAlgorithm;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface Account extends Resource<
|
|
51
|
+
"ACME.Account",
|
|
52
|
+
AccountProps,
|
|
53
|
+
{
|
|
54
|
+
/** The CA's directory URL. */
|
|
55
|
+
directoryUrl: string;
|
|
56
|
+
/** PEM root trusted for the CA's HTTPS endpoint (private CAs only). */
|
|
57
|
+
trustedRoot: string | undefined;
|
|
58
|
+
/** The account URL the CA assigned — the `kid` on every later request. */
|
|
59
|
+
accountUrl: string;
|
|
60
|
+
/** Observed account status (`valid`, `deactivated`, `revoked`). */
|
|
61
|
+
status: string;
|
|
62
|
+
/** Observed contact URLs. */
|
|
63
|
+
contact: string[];
|
|
64
|
+
/** Account key algorithm. */
|
|
65
|
+
keyAlgorithm: KeyAlgorithm;
|
|
66
|
+
/** The account's private JWK (JSON). Sensitive; persisted in stack state. */
|
|
67
|
+
privateKey: Redacted.Redacted<string>;
|
|
68
|
+
},
|
|
69
|
+
never,
|
|
70
|
+
Providers
|
|
71
|
+
> {}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* An ACME account with a certificate authority — the identity that every
|
|
75
|
+
* {@link Certificate} and every runtime {@link IssueCertificate} call
|
|
76
|
+
* signs with. One per CA per stack.
|
|
77
|
+
*
|
|
78
|
+
* The account key is generated on first reconcile and persisted as a
|
|
79
|
+
* redacted value alongside the CA's account URL. Protect the state store
|
|
80
|
+
* as secret material; redaction alone does not encrypt the storage backend.
|
|
81
|
+
*
|
|
82
|
+
* ### Let's Encrypt
|
|
83
|
+
* **Example:** An account with Let's Encrypt
|
|
84
|
+
* ```typescript
|
|
85
|
+
* export const LetsEncrypt = ACME.Account("LetsEncrypt", {
|
|
86
|
+
* ca: ACME.LetsEncrypt,
|
|
87
|
+
* contact: ["mailto:ops@example.com"],
|
|
88
|
+
* termsOfServiceAgreed: true,
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* ### External Account Binding
|
|
93
|
+
* ZeroSSL and Google Trust Services bind ACME accounts to an existing
|
|
94
|
+
* customer account with a key id + HMAC pair from their dashboard (ZeroSSL
|
|
95
|
+
* also mints them over its REST API — see `@distilled.cloud/zerossl`).
|
|
96
|
+
*
|
|
97
|
+
* **Example:** ZeroSSL
|
|
98
|
+
* ```typescript
|
|
99
|
+
* export const ZeroSsl = ACME.Account("ZeroSSL", {
|
|
100
|
+
* ca: ACME.ZeroSSL,
|
|
101
|
+
* eab: {
|
|
102
|
+
* keyId: Config.String("ZEROSSL_EAB_KID"),
|
|
103
|
+
* hmacKey: Config.Redacted("ZEROSSL_EAB_HMAC_KEY"),
|
|
104
|
+
* },
|
|
105
|
+
* termsOfServiceAgreed: true,
|
|
106
|
+
* });
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* ### Private CAs
|
|
110
|
+
* A private CA (Pebble, step-ca) whose HTTPS certificate is not publicly
|
|
111
|
+
* trusted passes its root as `trustedRoot`.
|
|
112
|
+
*
|
|
113
|
+
* **Example:** Pebble
|
|
114
|
+
* ```typescript
|
|
115
|
+
* export const Pebble = ACME.Account("Pebble", {
|
|
116
|
+
* ca: { directoryUrl: "https://localhost:14000/dir", trustedRoot: pebbleRootPem },
|
|
117
|
+
* termsOfServiceAgreed: true,
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @resource
|
|
122
|
+
* @product Account
|
|
123
|
+
* @category Certificates
|
|
124
|
+
*/
|
|
125
|
+
export const Account = Resource<Account>("ACME.Account");
|
|
126
|
+
|
|
127
|
+
/** The CA answered `newAccount` without a `Location`, so the account has no URL. */
|
|
128
|
+
export class AccountUrlMissing extends Data.TaggedError("ACME.AccountUrlMissing")<{
|
|
129
|
+
readonly directoryUrl: string;
|
|
130
|
+
}> {}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Normalize to exactly one `Redacted` layer: a value that already came
|
|
134
|
+
* redacted (an SDK's sensitive field) wrapped again by the caller would
|
|
135
|
+
* otherwise sign with an object instead of the key.
|
|
136
|
+
*/
|
|
137
|
+
const unwrap = (value: Redacted.Redacted<string> | string) => {
|
|
138
|
+
let inner: unknown = value;
|
|
139
|
+
while (Redacted.isRedacted(inner)) inner = Redacted.value(inner);
|
|
140
|
+
return Redacted.make(String(inner));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const credentialsOf = (
|
|
144
|
+
props: Pick<AccountProps, "ca" | "eab">,
|
|
145
|
+
accountKey: Redacted.Redacted<string>,
|
|
146
|
+
accountUrl: string | undefined,
|
|
147
|
+
): AccountCredentials => ({
|
|
148
|
+
ca: props.ca,
|
|
149
|
+
accountKey,
|
|
150
|
+
accountUrl,
|
|
151
|
+
externalAccountBinding:
|
|
152
|
+
props.eab === undefined
|
|
153
|
+
? undefined
|
|
154
|
+
: { keyId: props.eab.keyId, hmacKey: unwrap(props.eab.hmacKey) },
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** `newAccount` with `onlyReturnExisting` — the account, or `undefined`. */
|
|
158
|
+
const observeAccount = (credentials: AccountCredentials) =>
|
|
159
|
+
acme.newAccount({ onlyReturnExisting: true }).pipe(
|
|
160
|
+
Effect.catchTag("AcmeAccountDoesNotExist", () => Effect.undefined),
|
|
161
|
+
Effect.provide(accountLayer(credentials)),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const sameContacts = (
|
|
165
|
+
a: ReadonlyArray<string> | undefined,
|
|
166
|
+
b: ReadonlyArray<string> | undefined,
|
|
167
|
+
) => {
|
|
168
|
+
const left = [...(a ?? [])].sort();
|
|
169
|
+
const right = [...(b ?? [])].sort();
|
|
170
|
+
return left.length === right.length && left.every((v, i) => v === right[i]);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const AccountProvider = () =>
|
|
174
|
+
Provider.succeed(Account, {
|
|
175
|
+
stables: ["directoryUrl", "accountUrl", "keyAlgorithm", "privateKey"],
|
|
176
|
+
|
|
177
|
+
diff: Effect.fn(function* ({ news, olds, output }) {
|
|
178
|
+
if (news === undefined || !isResolved(news) || output === undefined) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
if (
|
|
182
|
+
news.ca.directoryUrl !== output.directoryUrl ||
|
|
183
|
+
(news.keyAlgorithm ?? "ES256") !== output.keyAlgorithm ||
|
|
184
|
+
news.eab?.keyId !== olds?.eab?.keyId
|
|
185
|
+
) {
|
|
186
|
+
return { action: "replace" as const };
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}),
|
|
190
|
+
|
|
191
|
+
read: Effect.fn(function* ({ olds, output }) {
|
|
192
|
+
if (output === undefined || olds === undefined) return undefined;
|
|
193
|
+
const credentials = credentialsOf(olds, output.privateKey, output.accountUrl);
|
|
194
|
+
const existing = yield* observeAccount(credentials);
|
|
195
|
+
if (existing === undefined) return undefined;
|
|
196
|
+
return {
|
|
197
|
+
...output,
|
|
198
|
+
status: existing.status,
|
|
199
|
+
contact: existing.contact ?? [],
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
|
|
203
|
+
reconcile: Effect.fn(function* ({ news, output }) {
|
|
204
|
+
const keyAlgorithm = news.keyAlgorithm ?? "ES256";
|
|
205
|
+
// Observe: the key is the identity; keep it across reconciles.
|
|
206
|
+
const accountKey = output?.privateKey ?? (yield* Acme.Jose.generateAccountKey(keyAlgorithm));
|
|
207
|
+
const credentials = credentialsOf(news, accountKey, output?.accountUrl);
|
|
208
|
+
let account = yield* observeAccount(credentials);
|
|
209
|
+
|
|
210
|
+
// Repeating newAccount with the same key returns the existing account.
|
|
211
|
+
if (account === undefined) {
|
|
212
|
+
account = yield* acme
|
|
213
|
+
.newAccount({
|
|
214
|
+
contact: news.contact,
|
|
215
|
+
termsOfServiceAgreed: true,
|
|
216
|
+
})
|
|
217
|
+
.pipe(Effect.provide(accountLayer(credentials)));
|
|
218
|
+
}
|
|
219
|
+
const accountUrl = account.location ?? output?.accountUrl;
|
|
220
|
+
if (accountUrl === undefined) {
|
|
221
|
+
return yield* new AccountUrlMissing({
|
|
222
|
+
directoryUrl: news.ca.directoryUrl,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Sync: contacts are the only mutable aspect.
|
|
227
|
+
const withKid = accountLayer(credentialsOf(news, accountKey, accountUrl));
|
|
228
|
+
if (!sameContacts(news.contact, account.contact)) {
|
|
229
|
+
account = yield* acme
|
|
230
|
+
.updateAccount({ url: accountUrl, contact: news.contact ?? [] })
|
|
231
|
+
.pipe(Effect.provide(withKid));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
directoryUrl: news.ca.directoryUrl,
|
|
236
|
+
trustedRoot: news.ca.trustedRoot,
|
|
237
|
+
accountUrl,
|
|
238
|
+
status: account.status,
|
|
239
|
+
contact: account.contact ?? [],
|
|
240
|
+
keyAlgorithm,
|
|
241
|
+
privateKey: accountKey,
|
|
242
|
+
};
|
|
243
|
+
}),
|
|
244
|
+
|
|
245
|
+
delete: Effect.fn(function* ({ olds, output }) {
|
|
246
|
+
const credentials = credentialsOf(olds, output.privateKey, output.accountUrl);
|
|
247
|
+
// Deactivation is permanent and idempotent: a deactivated account
|
|
248
|
+
// answers `unauthorized`, a purged one `accountDoesNotExist`.
|
|
249
|
+
yield* acme.updateAccount({ url: output.accountUrl, status: "deactivated" }).pipe(
|
|
250
|
+
Effect.catchTag(["AcmeAccountDoesNotExist", "AcmeUnauthorized"], () => Effect.undefined),
|
|
251
|
+
Effect.provide(accountLayer(credentials)),
|
|
252
|
+
);
|
|
253
|
+
}),
|
|
254
|
+
});
|