euclid-ndk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +603 -0
- package/dist/auth/index.d.ts +18 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +17 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/rfc9421.d.ts +112 -0
- package/dist/auth/rfc9421.d.ts.map +1 -0
- package/dist/auth/rfc9421.js +452 -0
- package/dist/auth/rfc9421.js.map +1 -0
- package/dist/auth/scheme.d.ts +46 -0
- package/dist/auth/scheme.d.ts.map +1 -0
- package/dist/auth/scheme.js +44 -0
- package/dist/auth/scheme.js.map +1 -0
- package/dist/auth/signable.d.ts +37 -0
- package/dist/auth/signable.d.ts.map +1 -0
- package/dist/auth/signable.js +70 -0
- package/dist/auth/signable.js.map +1 -0
- package/dist/auth/sigv4.d.ts +72 -0
- package/dist/auth/sigv4.d.ts.map +1 -0
- package/dist/auth/sigv4.js +227 -0
- package/dist/auth/sigv4.js.map +1 -0
- package/dist/credentials.d.ts +56 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +135 -0
- package/dist/credentials.js.map +1 -0
- package/dist/dto/com.d.ts +101 -0
- package/dist/dto/com.d.ts.map +1 -0
- package/dist/dto/com.js +132 -0
- package/dist/dto/com.js.map +1 -0
- package/dist/dto/eag.d.ts +104 -0
- package/dist/dto/eag.d.ts.map +1 -0
- package/dist/dto/eag.js +70 -0
- package/dist/dto/eag.js.map +1 -0
- package/dist/dto/eam.d.ts +105 -0
- package/dist/dto/eam.d.ts.map +1 -0
- package/dist/dto/eam.js +98 -0
- package/dist/dto/eam.js.map +1 -0
- package/dist/dto/eap.d.ts +88 -0
- package/dist/dto/eap.d.ts.map +1 -0
- package/dist/dto/eap.js +54 -0
- package/dist/dto/eap.js.map +1 -0
- package/dist/dto/ekm.d.ts +109 -0
- package/dist/dto/ekm.d.ts.map +1 -0
- package/dist/dto/ekm.js +78 -0
- package/dist/dto/ekm.js.map +1 -0
- package/dist/dto/ekv.d.ts +73 -0
- package/dist/dto/ekv.d.ts.map +1 -0
- package/dist/dto/ekv.js +45 -0
- package/dist/dto/ekv.js.map +1 -0
- package/dist/dto/ens.d.ts +84 -0
- package/dist/dto/ens.d.ts.map +1 -0
- package/dist/dto/ens.js +72 -0
- package/dist/dto/ens.js.map +1 -0
- package/dist/dto/eqs.d.ts +160 -0
- package/dist/dto/eqs.d.ts.map +1 -0
- package/dist/dto/eqs.js +120 -0
- package/dist/dto/eqs.js.map +1 -0
- package/dist/dto/esm.d.ts +192 -0
- package/dist/dto/esm.d.ts.map +1 -0
- package/dist/dto/esm.js +147 -0
- package/dist/dto/esm.js.map +1 -0
- package/dist/dto/ess.d.ts +49 -0
- package/dist/dto/ess.d.ts.map +1 -0
- package/dist/dto/ess.js +30 -0
- package/dist/dto/ess.js.map +1 -0
- package/dist/dto/json.d.ts +24 -0
- package/dist/dto/json.d.ts.map +1 -0
- package/dist/dto/json.js +47 -0
- package/dist/dto/json.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/http/client.d.ts +96 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +258 -0
- package/dist/http/client.js.map +1 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/base.d.ts +121 -0
- package/dist/modules/base.d.ts.map +1 -0
- package/dist/modules/base.js +142 -0
- package/dist/modules/base.js.map +1 -0
- package/dist/modules/eag.d.ts +166 -0
- package/dist/modules/eag.d.ts.map +1 -0
- package/dist/modules/eag.js +189 -0
- package/dist/modules/eag.js.map +1 -0
- package/dist/modules/eam.d.ts +314 -0
- package/dist/modules/eam.d.ts.map +1 -0
- package/dist/modules/eam.js +641 -0
- package/dist/modules/eam.js.map +1 -0
- package/dist/modules/eap.d.ts +204 -0
- package/dist/modules/eap.d.ts.map +1 -0
- package/dist/modules/eap.js +218 -0
- package/dist/modules/eap.js.map +1 -0
- package/dist/modules/ekm.d.ts +182 -0
- package/dist/modules/ekm.d.ts.map +1 -0
- package/dist/modules/ekm.js +228 -0
- package/dist/modules/ekm.js.map +1 -0
- package/dist/modules/ekv.d.ts +189 -0
- package/dist/modules/ekv.d.ts.map +1 -0
- package/dist/modules/ekv.js +212 -0
- package/dist/modules/ekv.js.map +1 -0
- package/dist/modules/ens.d.ts +122 -0
- package/dist/modules/ens.d.ts.map +1 -0
- package/dist/modules/ens.js +167 -0
- package/dist/modules/ens.js.map +1 -0
- package/dist/modules/eqs.d.ts +283 -0
- package/dist/modules/eqs.d.ts.map +1 -0
- package/dist/modules/eqs.js +355 -0
- package/dist/modules/eqs.js.map +1 -0
- package/dist/modules/esm.d.ts +358 -0
- package/dist/modules/esm.d.ts.map +1 -0
- package/dist/modules/esm.js +660 -0
- package/dist/modules/esm.js.map +1 -0
- package/dist/modules/ess.d.ts +96 -0
- package/dist/modules/ess.d.ts.map +1 -0
- package/dist/modules/ess.js +110 -0
- package/dist/modules/ess.js.map +1 -0
- package/dist/url.d.ts +31 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +47 -0
- package/dist/url.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 9421 HTTP Message Signatures, the scheme meant to take over from SigV4.
|
|
3
|
+
*
|
|
4
|
+
* Same credentials, same threat model, standard wire format: the caller still presents an access
|
|
5
|
+
* key ID and its secret, and the signature is still an HMAC-SHA256 over a canonical rendering of
|
|
6
|
+
* the request, but what gets rendered and how it travels is the IETF's rather than AWS's. The
|
|
7
|
+
* signature goes in `Signature` and `Signature-Input` instead of `Authorization`, which is what
|
|
8
|
+
* lets the two schemes coexist during a migration - SigV4 owns `Authorization`, this owns its own
|
|
9
|
+
* headers, and a server can accept either without guessing which one a request meant.
|
|
10
|
+
*
|
|
11
|
+
* **Algorithm.** Only `hmac-sha256`, because that is the algorithm that takes euclid's existing
|
|
12
|
+
* access-key/secret pairs unchanged: the HMAC key is the secret's UTF-8 bytes directly (RFC 9421
|
|
13
|
+
* §3.3.3), with none of SigV4's key-derivation chain to reproduce.
|
|
14
|
+
*
|
|
15
|
+
* **Covered components.** RFC 9421 lets a signer choose what its signature covers and declare it
|
|
16
|
+
* in `Signature-Input`. Taken literally that would let a request decide how little of itself to
|
|
17
|
+
* authenticate, so it is not taken literally here: the set is fixed, {@link verify} rejects a
|
|
18
|
+
* signature covering anything else in any other order, and {@link sign} refuses to sign a request
|
|
19
|
+
* that cannot supply all of it. The list is a wire format shared with euclid's
|
|
20
|
+
* `Core::HttpSignature::CoveredComponents()` and the two only ever change together.
|
|
21
|
+
*
|
|
22
|
+
* See RFC 9421 and RFC 9530 (Content-Digest).
|
|
23
|
+
*/
|
|
24
|
+
import type { SignableRequest } from "./signable.js";
|
|
25
|
+
export declare const ALGORITHM = "hmac-sha256";
|
|
26
|
+
export declare const TAG = "euclid";
|
|
27
|
+
export declare const SIGNATURE_HEADER_NAMES: readonly ["Content-Digest", "Signature-Input", "Signature"];
|
|
28
|
+
/**
|
|
29
|
+
* The parameters of one signature, as carried in `Signature-Input`.
|
|
30
|
+
*
|
|
31
|
+
* `raw` is the parameter string exactly as received. It is kept because that is what the signature
|
|
32
|
+
* was computed over: re-serialising the parsed fields would not reliably reproduce another
|
|
33
|
+
* implementation's byte-for-byte choices, and a base rebuilt from a re-serialisation would fail to
|
|
34
|
+
* verify a perfectly good signature.
|
|
35
|
+
*/
|
|
36
|
+
export interface SignatureParams {
|
|
37
|
+
components: string[];
|
|
38
|
+
created: number | null;
|
|
39
|
+
expires: number | null;
|
|
40
|
+
keyId: string;
|
|
41
|
+
algorithm: string | null;
|
|
42
|
+
nonce: string | null;
|
|
43
|
+
tag: string | null;
|
|
44
|
+
raw: string;
|
|
45
|
+
}
|
|
46
|
+
/** A signature and its parameters, paired by the dictionary label they share. */
|
|
47
|
+
export interface ParsedSignature {
|
|
48
|
+
label: string;
|
|
49
|
+
params: SignatureParams;
|
|
50
|
+
signature: Buffer;
|
|
51
|
+
}
|
|
52
|
+
/** The components every euclid signature covers, in signing order. */
|
|
53
|
+
export declare function requiredComponents(): readonly string[];
|
|
54
|
+
/**
|
|
55
|
+
* The components a signature over this request must cover.
|
|
56
|
+
*
|
|
57
|
+
* Takes the request because RFC 9421 allows a signer to cover a field only when the message carries
|
|
58
|
+
* one, and a future version of euclid may want that back. It does not vary today.
|
|
59
|
+
*/
|
|
60
|
+
export declare function coveredComponents(_req: SignableRequest): readonly string[];
|
|
61
|
+
/** The headers {@link sign} writes, in the case they should be sent in. */
|
|
62
|
+
export declare function signatureHeaderNames(): readonly string[];
|
|
63
|
+
/** The RFC 9530 `Content-Digest` value binding a body to its signature. */
|
|
64
|
+
export declare function contentDigest(body: Buffer | string): string;
|
|
65
|
+
/** Serialises the `Signature-Input` value for a set of covered components, without a label. */
|
|
66
|
+
export declare function serializeSignatureParams(components: readonly string[], created: number, keyId: string, nonce: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Builds the §2.5 signature base: one line per component, then `@signature-params`.
|
|
69
|
+
*
|
|
70
|
+
* The last line carries no trailing newline. Answers null if any covered component is absent from
|
|
71
|
+
* the request - §2.1 forbids signing over a field that is not there, and producing a base with a
|
|
72
|
+
* blank in its place would sign something the verifier will never reconstruct.
|
|
73
|
+
*/
|
|
74
|
+
export declare function signatureBase(req: SignableRequest, components: readonly string[], params: string): string | null;
|
|
75
|
+
/**
|
|
76
|
+
* Signs a request in place: sets Content-Digest, Signature-Input and Signature.
|
|
77
|
+
*
|
|
78
|
+
* Call it once every header the signature covers and the body are already set. Unlike SigV4 this
|
|
79
|
+
* leaves Authorization alone, so a request can carry a bearer token and a signature at once if a
|
|
80
|
+
* deployment wants both during a migration.
|
|
81
|
+
*
|
|
82
|
+
* Throws when a component that must be covered is missing, which for a client means it was
|
|
83
|
+
* configured without a region, an account id or a user id and so has nothing to sign with.
|
|
84
|
+
*/
|
|
85
|
+
export declare function sign(req: SignableRequest, accessKeyId: string, secretAccessKey: string): void;
|
|
86
|
+
/**
|
|
87
|
+
* Verifies an RFC 9421-signed request, answering the key ID it was signed with.
|
|
88
|
+
*
|
|
89
|
+
* Rebuilds the base from the request exactly as received - including the parameters verbatim - and
|
|
90
|
+
* compares the HMAC in constant time. Answers null on every failure without saying which, as
|
|
91
|
+
* {@link import("./sigv4.js").verify} does.
|
|
92
|
+
*
|
|
93
|
+
* The checks mirror euclid's `Core::HttpSignature::Verify`: the covered set must be exactly
|
|
94
|
+
* {@link requiredComponents}, in that order; `alg` must be `hmac-sha256`; `created` must be within
|
|
95
|
+
* the skew window; and `Content-Digest` must match the body actually received, since that header is
|
|
96
|
+
* the only thing the signature covers the body through.
|
|
97
|
+
*
|
|
98
|
+
* `tag` is checked only when present, and euclid's own signer emits none. Requiring it would reject
|
|
99
|
+
* signatures made by the server itself.
|
|
100
|
+
*/
|
|
101
|
+
export declare function verify(req: SignableRequest, lookupSecret: (keyId: string) => string | null | undefined, maxSkewSeconds?: number): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Parses a matching pair of `Signature-Input` and `Signature` values.
|
|
104
|
+
*
|
|
105
|
+
* Both must hold exactly one signature under the same label. RFC 9421 allows several - a proxy
|
|
106
|
+
* adding its own alongside the client's - but euclid has no use for more than one, and refusing to
|
|
107
|
+
* choose among them means never verifying the wrong one.
|
|
108
|
+
*/
|
|
109
|
+
export declare function parseSignature(signatureInputHeader: string, signatureHeader: string): ParsedSignature | null;
|
|
110
|
+
/** Parses a `Signature-Input` value: an inner list of components, then the parameters. */
|
|
111
|
+
export declare function parseSignatureParams(value: string): SignatureParams | null;
|
|
112
|
+
//# sourceMappingURL=rfc9421.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rfc9421.d.ts","sourceRoot":"","sources":["../../src/auth/rfc9421.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,eAAO,MAAM,SAAS,gBAAgB,CAAC;AACvC,eAAO,MAAM,GAAG,WAAW,CAAC;AA2B5B,eAAO,MAAM,sBAAsB,6DAA8D,CAAC;AAElG;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CAE1E;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,IAAI,SAAS,MAAM,EAAE,CAExD;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAG3D;AAED,+FAA+F;AAC/F,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,MAAM,CAGR;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAShH;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAiB7F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CACpB,GAAG,EAAE,eAAe,EACpB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,EAC1D,cAAc,GAAE,MAAiC,GAChD,MAAM,GAAG,IAAI,CA+Bf;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAS5G;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA+C1E"}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 9421 HTTP Message Signatures, the scheme meant to take over from SigV4.
|
|
3
|
+
*
|
|
4
|
+
* Same credentials, same threat model, standard wire format: the caller still presents an access
|
|
5
|
+
* key ID and its secret, and the signature is still an HMAC-SHA256 over a canonical rendering of
|
|
6
|
+
* the request, but what gets rendered and how it travels is the IETF's rather than AWS's. The
|
|
7
|
+
* signature goes in `Signature` and `Signature-Input` instead of `Authorization`, which is what
|
|
8
|
+
* lets the two schemes coexist during a migration - SigV4 owns `Authorization`, this owns its own
|
|
9
|
+
* headers, and a server can accept either without guessing which one a request meant.
|
|
10
|
+
*
|
|
11
|
+
* **Algorithm.** Only `hmac-sha256`, because that is the algorithm that takes euclid's existing
|
|
12
|
+
* access-key/secret pairs unchanged: the HMAC key is the secret's UTF-8 bytes directly (RFC 9421
|
|
13
|
+
* §3.3.3), with none of SigV4's key-derivation chain to reproduce.
|
|
14
|
+
*
|
|
15
|
+
* **Covered components.** RFC 9421 lets a signer choose what its signature covers and declare it
|
|
16
|
+
* in `Signature-Input`. Taken literally that would let a request decide how little of itself to
|
|
17
|
+
* authenticate, so it is not taken literally here: the set is fixed, {@link verify} rejects a
|
|
18
|
+
* signature covering anything else in any other order, and {@link sign} refuses to sign a request
|
|
19
|
+
* that cannot supply all of it. The list is a wire format shared with euclid's
|
|
20
|
+
* `Core::HttpSignature::CoveredComponents()` and the two only ever change together.
|
|
21
|
+
*
|
|
22
|
+
* See RFC 9421 and RFC 9530 (Content-Digest).
|
|
23
|
+
*/
|
|
24
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
25
|
+
export const ALGORITHM = "hmac-sha256";
|
|
26
|
+
export const TAG = "euclid";
|
|
27
|
+
const LABEL = "sig1";
|
|
28
|
+
const DEFAULT_MAX_SKEW_SECONDS = 15 * 60;
|
|
29
|
+
/**
|
|
30
|
+
* The derived components pin the request line and host, content-digest pins the body, and the
|
|
31
|
+
* x-euclid-* headers pin what the request asks for and on whose behalf - which for euclid lives in
|
|
32
|
+
* headers rather than in the URI.
|
|
33
|
+
*
|
|
34
|
+
* Two absences are deliberate. "@query" is not covered because euclid POSTs everything to "/" and
|
|
35
|
+
* carries its arguments in the body, so there is no query string to protect. "x-euclid-namespace"
|
|
36
|
+
* is not covered either, and that one is a real gap rather than a simplification: the namespace
|
|
37
|
+
* scopes what a request may touch and it currently travels unsigned. Closing it means adding the
|
|
38
|
+
* component to euclid's server and to this list in the same release.
|
|
39
|
+
*/
|
|
40
|
+
const REQUIRED_COMPONENTS = [
|
|
41
|
+
"@method",
|
|
42
|
+
"@path",
|
|
43
|
+
"@authority",
|
|
44
|
+
"content-digest",
|
|
45
|
+
"x-euclid-account-id",
|
|
46
|
+
"x-euclid-action",
|
|
47
|
+
"x-euclid-region",
|
|
48
|
+
"x-euclid-target",
|
|
49
|
+
"x-euclid-user-id",
|
|
50
|
+
];
|
|
51
|
+
export const SIGNATURE_HEADER_NAMES = ["Content-Digest", "Signature-Input", "Signature"];
|
|
52
|
+
/** The components every euclid signature covers, in signing order. */
|
|
53
|
+
export function requiredComponents() {
|
|
54
|
+
return REQUIRED_COMPONENTS;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The components a signature over this request must cover.
|
|
58
|
+
*
|
|
59
|
+
* Takes the request because RFC 9421 allows a signer to cover a field only when the message carries
|
|
60
|
+
* one, and a future version of euclid may want that back. It does not vary today.
|
|
61
|
+
*/
|
|
62
|
+
export function coveredComponents(_req) {
|
|
63
|
+
return REQUIRED_COMPONENTS;
|
|
64
|
+
}
|
|
65
|
+
/** The headers {@link sign} writes, in the case they should be sent in. */
|
|
66
|
+
export function signatureHeaderNames() {
|
|
67
|
+
return SIGNATURE_HEADER_NAMES;
|
|
68
|
+
}
|
|
69
|
+
/** The RFC 9530 `Content-Digest` value binding a body to its signature. */
|
|
70
|
+
export function contentDigest(body) {
|
|
71
|
+
const raw = typeof body === "string" ? Buffer.from(body, "utf8") : body;
|
|
72
|
+
return `sha-256=:${createHash("sha256").update(raw).digest("base64")}:`;
|
|
73
|
+
}
|
|
74
|
+
/** Serialises the `Signature-Input` value for a set of covered components, without a label. */
|
|
75
|
+
export function serializeSignatureParams(components, created, keyId, nonce) {
|
|
76
|
+
const inner = components.map(quote).join(" ");
|
|
77
|
+
return `(${inner});created=${created};keyid=${quote(keyId)};alg=${quote(ALGORITHM)};nonce=${quote(nonce)};tag=${quote(TAG)}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Builds the §2.5 signature base: one line per component, then `@signature-params`.
|
|
81
|
+
*
|
|
82
|
+
* The last line carries no trailing newline. Answers null if any covered component is absent from
|
|
83
|
+
* the request - §2.1 forbids signing over a field that is not there, and producing a base with a
|
|
84
|
+
* blank in its place would sign something the verifier will never reconstruct.
|
|
85
|
+
*/
|
|
86
|
+
export function signatureBase(req, components, params) {
|
|
87
|
+
const lines = [];
|
|
88
|
+
for (const component of components) {
|
|
89
|
+
const value = componentValue(req, component);
|
|
90
|
+
if (value === null)
|
|
91
|
+
return null;
|
|
92
|
+
lines.push(`"${component}": ${value}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push(`"@signature-params": ${params}`);
|
|
95
|
+
return lines.join("\n");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Signs a request in place: sets Content-Digest, Signature-Input and Signature.
|
|
99
|
+
*
|
|
100
|
+
* Call it once every header the signature covers and the body are already set. Unlike SigV4 this
|
|
101
|
+
* leaves Authorization alone, so a request can carry a bearer token and a signature at once if a
|
|
102
|
+
* deployment wants both during a migration.
|
|
103
|
+
*
|
|
104
|
+
* Throws when a component that must be covered is missing, which for a client means it was
|
|
105
|
+
* configured without a region, an account id or a user id and so has nothing to sign with.
|
|
106
|
+
*/
|
|
107
|
+
export function sign(req, accessKeyId, secretAccessKey) {
|
|
108
|
+
req.header("Content-Digest", contentDigest(req.body));
|
|
109
|
+
const components = coveredComponents(req);
|
|
110
|
+
const params = serializeSignatureParams(components, Math.floor(Date.now() / 1000), accessKeyId, newNonce());
|
|
111
|
+
const base = signatureBase(req, components, params);
|
|
112
|
+
if (base === null) {
|
|
113
|
+
const missing = components.filter((component) => componentValue(req, component) === null);
|
|
114
|
+
throw new Error(`request cannot be signed, it is missing ${JSON.stringify(missing)} - euclid signs a fixed set of ` +
|
|
115
|
+
"components, so a client configured without a region, account id or user id has nothing to sign with");
|
|
116
|
+
}
|
|
117
|
+
const signature = createHmac("sha256", Buffer.from(secretAccessKey, "utf8")).update(base, "utf8").digest();
|
|
118
|
+
req.header("Signature-Input", `${LABEL}=${params}`);
|
|
119
|
+
req.header("Signature", `${LABEL}=:${signature.toString("base64")}:`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Verifies an RFC 9421-signed request, answering the key ID it was signed with.
|
|
123
|
+
*
|
|
124
|
+
* Rebuilds the base from the request exactly as received - including the parameters verbatim - and
|
|
125
|
+
* compares the HMAC in constant time. Answers null on every failure without saying which, as
|
|
126
|
+
* {@link import("./sigv4.js").verify} does.
|
|
127
|
+
*
|
|
128
|
+
* The checks mirror euclid's `Core::HttpSignature::Verify`: the covered set must be exactly
|
|
129
|
+
* {@link requiredComponents}, in that order; `alg` must be `hmac-sha256`; `created` must be within
|
|
130
|
+
* the skew window; and `Content-Digest` must match the body actually received, since that header is
|
|
131
|
+
* the only thing the signature covers the body through.
|
|
132
|
+
*
|
|
133
|
+
* `tag` is checked only when present, and euclid's own signer emits none. Requiring it would reject
|
|
134
|
+
* signatures made by the server itself.
|
|
135
|
+
*/
|
|
136
|
+
export function verify(req, lookupSecret, maxSkewSeconds = DEFAULT_MAX_SKEW_SECONDS) {
|
|
137
|
+
const parsed = parseSignature(req.get("signature-input"), req.get("signature"));
|
|
138
|
+
if (parsed === null)
|
|
139
|
+
return null;
|
|
140
|
+
const params = parsed.params;
|
|
141
|
+
// Only one algorithm exists here, so an unexpected alg is a rejection rather than a branch -
|
|
142
|
+
// which is also what stops a signature being reinterpreted under a weaker one.
|
|
143
|
+
if (params.algorithm !== ALGORITHM)
|
|
144
|
+
return null;
|
|
145
|
+
if (params.tag !== null && params.tag !== TAG)
|
|
146
|
+
return null;
|
|
147
|
+
// Fixed policy, not client-negotiated, and order-sensitive because the server is.
|
|
148
|
+
const required = coveredComponents(req);
|
|
149
|
+
if (params.components.length !== required.length)
|
|
150
|
+
return null;
|
|
151
|
+
if (params.components.some((component, index) => component !== required[index]))
|
|
152
|
+
return null;
|
|
153
|
+
const now = Math.floor(Date.now() / 1000);
|
|
154
|
+
if (params.created === null || Math.abs(now - params.created) > maxSkewSeconds)
|
|
155
|
+
return null;
|
|
156
|
+
if (params.expires !== null && params.expires < now)
|
|
157
|
+
return null;
|
|
158
|
+
if (!req.has("content-digest") || !equal(req.get("content-digest"), contentDigest(req.body)))
|
|
159
|
+
return null;
|
|
160
|
+
const secret = lookupSecret(params.keyId);
|
|
161
|
+
if (secret == null)
|
|
162
|
+
return null;
|
|
163
|
+
const base = signatureBase(req, params.components, params.raw);
|
|
164
|
+
if (base === null)
|
|
165
|
+
return null;
|
|
166
|
+
const expected = createHmac("sha256", Buffer.from(secret, "utf8")).update(base, "utf8").digest();
|
|
167
|
+
if (expected.length !== parsed.signature.length || !timingSafeEqual(expected, parsed.signature))
|
|
168
|
+
return null;
|
|
169
|
+
return params.keyId;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Parses a matching pair of `Signature-Input` and `Signature` values.
|
|
173
|
+
*
|
|
174
|
+
* Both must hold exactly one signature under the same label. RFC 9421 allows several - a proxy
|
|
175
|
+
* adding its own alongside the client's - but euclid has no use for more than one, and refusing to
|
|
176
|
+
* choose among them means never verifying the wrong one.
|
|
177
|
+
*/
|
|
178
|
+
export function parseSignature(signatureInputHeader, signatureHeader) {
|
|
179
|
+
const inputMember = singleDictionaryMember(signatureInputHeader);
|
|
180
|
+
const signatureMember = singleDictionaryMember(signatureHeader);
|
|
181
|
+
if (inputMember === null || signatureMember === null || inputMember[0] !== signatureMember[0])
|
|
182
|
+
return null;
|
|
183
|
+
const params = parseSignatureParams(inputMember[1]);
|
|
184
|
+
const signature = parseByteSequence(signatureMember[1]);
|
|
185
|
+
if (params === null || signature === null)
|
|
186
|
+
return null;
|
|
187
|
+
return { label: inputMember[0], params, signature };
|
|
188
|
+
}
|
|
189
|
+
/** Parses a `Signature-Input` value: an inner list of components, then the parameters. */
|
|
190
|
+
export function parseSignatureParams(value) {
|
|
191
|
+
if (!value || value[0] !== "(")
|
|
192
|
+
return null;
|
|
193
|
+
const components = [];
|
|
194
|
+
let pos = 1;
|
|
195
|
+
for (;;) {
|
|
196
|
+
if (pos >= value.length)
|
|
197
|
+
return null;
|
|
198
|
+
const char = value[pos];
|
|
199
|
+
if (char === ")") {
|
|
200
|
+
pos += 1;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (char === " ") {
|
|
204
|
+
pos += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (char !== '"')
|
|
208
|
+
return null;
|
|
209
|
+
const end = endOfQuotedString(value, pos);
|
|
210
|
+
if (end < 0)
|
|
211
|
+
return null;
|
|
212
|
+
// A component carrying parameters of its own ("x";req and friends) means something this module
|
|
213
|
+
// does not implement, so it is rejected rather than quietly read as plain.
|
|
214
|
+
if (end + 1 < value.length && value[end + 1] !== " " && value[end + 1] !== ")")
|
|
215
|
+
return null;
|
|
216
|
+
components.push(unquote(value.slice(pos, end + 1)));
|
|
217
|
+
pos = end + 1;
|
|
218
|
+
}
|
|
219
|
+
if (components.length === 0)
|
|
220
|
+
return null;
|
|
221
|
+
const parameters = parseParameters(value.slice(pos));
|
|
222
|
+
if (parameters === null)
|
|
223
|
+
return null;
|
|
224
|
+
const keyId = parameters["keyid"];
|
|
225
|
+
if (typeof keyId !== "string")
|
|
226
|
+
return null;
|
|
227
|
+
const created = timestampOf(parameters, "created");
|
|
228
|
+
const expires = timestampOf(parameters, "expires");
|
|
229
|
+
if (created === INVALID || expires === INVALID)
|
|
230
|
+
return null;
|
|
231
|
+
return {
|
|
232
|
+
components,
|
|
233
|
+
created,
|
|
234
|
+
expires,
|
|
235
|
+
keyId,
|
|
236
|
+
algorithm: stringOrNull(parameters["alg"]),
|
|
237
|
+
nonce: stringOrNull(parameters["nonce"]),
|
|
238
|
+
tag: stringOrNull(parameters["tag"]),
|
|
239
|
+
raw: value,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// -- internals -------------------------------------------------------------------------------
|
|
243
|
+
/** One covered component's value for the base: §2.2's derived values, or the header. */
|
|
244
|
+
function componentValue(req, component) {
|
|
245
|
+
if (!component.startsWith("@"))
|
|
246
|
+
return req.has(component) ? req.get(component) : null;
|
|
247
|
+
const question = req.target.indexOf("?");
|
|
248
|
+
const path = question < 0 ? req.target : req.target.slice(0, question);
|
|
249
|
+
const query = question < 0 ? null : req.target.slice(question + 1);
|
|
250
|
+
switch (component) {
|
|
251
|
+
case "@method":
|
|
252
|
+
return req.method.toUpperCase();
|
|
253
|
+
case "@authority":
|
|
254
|
+
return authorityOf(req);
|
|
255
|
+
case "@scheme":
|
|
256
|
+
return req.scheme;
|
|
257
|
+
case "@path":
|
|
258
|
+
// §2.2.6: the path is "/" when empty.
|
|
259
|
+
return path === "" ? "/" : path;
|
|
260
|
+
case "@query":
|
|
261
|
+
// §2.2.7: the query keeps its leading "?", and is that character alone when there is none.
|
|
262
|
+
return query === null ? "?" : `?${query}`;
|
|
263
|
+
case "@request-target":
|
|
264
|
+
return req.target;
|
|
265
|
+
case "@target-uri": {
|
|
266
|
+
const authority = authorityOf(req);
|
|
267
|
+
return authority === null ? null : `${req.scheme}://${authority}${req.target}`;
|
|
268
|
+
}
|
|
269
|
+
default:
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** §2.2.3: the host header lowercased, minus the port when it is the scheme's default. */
|
|
274
|
+
function authorityOf(req) {
|
|
275
|
+
const host = req.get("host").toLowerCase();
|
|
276
|
+
if (!host)
|
|
277
|
+
return null;
|
|
278
|
+
const defaultPort = req.scheme === "https" ? ":443" : req.scheme === "http" ? ":80" : "";
|
|
279
|
+
return defaultPort && host.endsWith(defaultPort) ? host.slice(0, -defaultPort.length) : host;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Splits a structured-field dictionary that must hold exactly one member into label and value.
|
|
283
|
+
*
|
|
284
|
+
* Respects quoting and nesting, so a comma inside a component name or a base64 signature cannot be
|
|
285
|
+
* mistaken for a member separator.
|
|
286
|
+
*/
|
|
287
|
+
function singleDictionaryMember(value) {
|
|
288
|
+
if (!value)
|
|
289
|
+
return null;
|
|
290
|
+
const members = splitTopLevel(value, ",");
|
|
291
|
+
if (members.length !== 1)
|
|
292
|
+
return null;
|
|
293
|
+
const member = members[0].trim();
|
|
294
|
+
const equals = indexOfTopLevel(member, "=");
|
|
295
|
+
if (equals <= 0 || equals === member.length - 1)
|
|
296
|
+
return null;
|
|
297
|
+
return [member.slice(0, equals).trim(), member.slice(equals + 1).trim()];
|
|
298
|
+
}
|
|
299
|
+
function splitTopLevel(value, delimiter) {
|
|
300
|
+
const parts = [];
|
|
301
|
+
let depth = 0;
|
|
302
|
+
let inQuotes = false;
|
|
303
|
+
let start = 0;
|
|
304
|
+
let i = 0;
|
|
305
|
+
while (i < value.length) {
|
|
306
|
+
const char = value[i];
|
|
307
|
+
if (inQuotes) {
|
|
308
|
+
if (char === "\\") {
|
|
309
|
+
i += 2;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (char === '"')
|
|
313
|
+
inQuotes = false;
|
|
314
|
+
}
|
|
315
|
+
else if (char === '"')
|
|
316
|
+
inQuotes = true;
|
|
317
|
+
else if (char === "(")
|
|
318
|
+
depth += 1;
|
|
319
|
+
else if (char === ")")
|
|
320
|
+
depth -= 1;
|
|
321
|
+
else if (char === delimiter && depth === 0) {
|
|
322
|
+
parts.push(value.slice(start, i));
|
|
323
|
+
start = i + 1;
|
|
324
|
+
}
|
|
325
|
+
i += 1;
|
|
326
|
+
}
|
|
327
|
+
parts.push(value.slice(start));
|
|
328
|
+
return parts;
|
|
329
|
+
}
|
|
330
|
+
function indexOfTopLevel(value, charToFind) {
|
|
331
|
+
let inQuotes = false;
|
|
332
|
+
let i = 0;
|
|
333
|
+
while (i < value.length) {
|
|
334
|
+
const char = value[i];
|
|
335
|
+
if (inQuotes) {
|
|
336
|
+
if (char === "\\") {
|
|
337
|
+
i += 2;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (char === '"')
|
|
341
|
+
inQuotes = false;
|
|
342
|
+
}
|
|
343
|
+
else if (char === '"')
|
|
344
|
+
inQuotes = true;
|
|
345
|
+
else if (char === charToFind)
|
|
346
|
+
return i;
|
|
347
|
+
i += 1;
|
|
348
|
+
}
|
|
349
|
+
return -1;
|
|
350
|
+
}
|
|
351
|
+
/** The index of the closing quote of the string opening at `start`, or -1 if unterminated. */
|
|
352
|
+
function endOfQuotedString(value, start) {
|
|
353
|
+
let i = start + 1;
|
|
354
|
+
while (i < value.length) {
|
|
355
|
+
if (value[i] === "\\") {
|
|
356
|
+
i += 2;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (value[i] === '"')
|
|
360
|
+
return i;
|
|
361
|
+
i += 1;
|
|
362
|
+
}
|
|
363
|
+
return -1;
|
|
364
|
+
}
|
|
365
|
+
/** Parses `;name=value;name2=value2` into a mapping, or null if it is not well-formed. */
|
|
366
|
+
function parseParameters(rest) {
|
|
367
|
+
const parameters = {};
|
|
368
|
+
let pos = 0;
|
|
369
|
+
while (pos < rest.length) {
|
|
370
|
+
if (rest[pos] !== ";")
|
|
371
|
+
return null;
|
|
372
|
+
pos += 1;
|
|
373
|
+
while (pos < rest.length && rest[pos] === " ")
|
|
374
|
+
pos += 1;
|
|
375
|
+
const nameStart = pos;
|
|
376
|
+
while (pos < rest.length && rest[pos] !== "=" && rest[pos] !== ";")
|
|
377
|
+
pos += 1;
|
|
378
|
+
const name = rest.slice(nameStart, pos).trim();
|
|
379
|
+
if (!name)
|
|
380
|
+
return null;
|
|
381
|
+
if (pos >= rest.length || rest[pos] === ";") {
|
|
382
|
+
// A bare parameter is a boolean true in structured fields.
|
|
383
|
+
parameters[name] = true;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
pos += 1; // the '='
|
|
387
|
+
if (pos < rest.length && rest[pos] === '"') {
|
|
388
|
+
const end = endOfQuotedString(rest, pos);
|
|
389
|
+
if (end < 0)
|
|
390
|
+
return null;
|
|
391
|
+
parameters[name] = unquote(rest.slice(pos, end + 1));
|
|
392
|
+
pos = end + 1;
|
|
393
|
+
}
|
|
394
|
+
else if (pos < rest.length && rest[pos] === ":") {
|
|
395
|
+
const end = rest.indexOf(":", pos + 1);
|
|
396
|
+
if (end < 0)
|
|
397
|
+
return null;
|
|
398
|
+
parameters[name] = decodeBase64(rest.slice(pos + 1, end));
|
|
399
|
+
pos = end + 1;
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
const valueStart = pos;
|
|
403
|
+
while (pos < rest.length && rest[pos] !== ";")
|
|
404
|
+
pos += 1;
|
|
405
|
+
parameters[name] = asIntegerOrToken(rest.slice(valueStart, pos).trim());
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return parameters;
|
|
409
|
+
}
|
|
410
|
+
/** Parses a structured-field byte sequence, `:<base64>:`. */
|
|
411
|
+
function parseByteSequence(value) {
|
|
412
|
+
const trimmed = value.trim();
|
|
413
|
+
if (trimmed.length < 2 || trimmed[0] !== ":" || trimmed[trimmed.length - 1] !== ":")
|
|
414
|
+
return null;
|
|
415
|
+
return decodeBase64(trimmed.slice(1, -1));
|
|
416
|
+
}
|
|
417
|
+
function decodeBase64(value) {
|
|
418
|
+
// Node's base64 decoder ignores what it cannot read rather than refusing it, so a round trip is
|
|
419
|
+
// what says the input really was base64 - the strictness the verifier depends on.
|
|
420
|
+
const decoded = Buffer.from(value, "base64");
|
|
421
|
+
return decoded.toString("base64").replace(/=+$/, "") === value.replace(/=+$/, "") ? decoded : null;
|
|
422
|
+
}
|
|
423
|
+
function asIntegerOrToken(raw) {
|
|
424
|
+
return /^-?\d+$/.test(raw) ? Number(raw) : raw;
|
|
425
|
+
}
|
|
426
|
+
const INVALID = Symbol("invalid");
|
|
427
|
+
/** The parameter as a Unix timestamp; null when absent, INVALID when present but not an integer. */
|
|
428
|
+
function timestampOf(parameters, name) {
|
|
429
|
+
if (!(name in parameters))
|
|
430
|
+
return null;
|
|
431
|
+
const value = parameters[name];
|
|
432
|
+
return typeof value === "number" && Number.isInteger(value) ? value : INVALID;
|
|
433
|
+
}
|
|
434
|
+
function stringOrNull(value) {
|
|
435
|
+
return typeof value === "string" ? value : null;
|
|
436
|
+
}
|
|
437
|
+
function quote(value) {
|
|
438
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
439
|
+
}
|
|
440
|
+
function unquote(quoted) {
|
|
441
|
+
return quoted.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
442
|
+
}
|
|
443
|
+
function newNonce() {
|
|
444
|
+
return randomBytes(16).toString("base64url");
|
|
445
|
+
}
|
|
446
|
+
/** Constant-time string comparison, which is what a digest check has to be. */
|
|
447
|
+
function equal(left, right) {
|
|
448
|
+
const a = Buffer.from(left, "utf8");
|
|
449
|
+
const b = Buffer.from(right, "utf8");
|
|
450
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
451
|
+
}
|
|
452
|
+
//# sourceMappingURL=rfc9421.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rfc9421.js","sourceRoot":"","sources":["../../src/auth/rfc9421.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Which request-signing scheme a client uses when it authenticates with an access key. */
|
|
2
|
+
import type { SignableRequest } from "./signable.js";
|
|
3
|
+
/**
|
|
4
|
+
* SigV4 or RFC 9421.
|
|
5
|
+
*
|
|
6
|
+
* Two schemes exist because euclid is moving from one to the other, not because both are wanted in
|
|
7
|
+
* the end. {@link SIGV4} is what euclid has always spoken and stays the default, so no existing
|
|
8
|
+
* caller changes behaviour; {@link RFC9421} is the standard scheme meant to replace it. Choosing
|
|
9
|
+
* one is a per-client decision, so a deployment can move one service at a time and roll back by
|
|
10
|
+
* changing a line rather than a release.
|
|
11
|
+
*
|
|
12
|
+
* Neither is consulted when a client authenticates with a bearer token: with no access key there is
|
|
13
|
+
* nothing to sign with, and the token goes in Authorization as before.
|
|
14
|
+
*
|
|
15
|
+
* The two do not collide on the wire - SigV4 puts its signature in Authorization, RFC 9421 in
|
|
16
|
+
* Signature/Signature-Input - so a server can accept both at once and tell which a request used.
|
|
17
|
+
* {@link signingSchemeOf} is that test.
|
|
18
|
+
*/
|
|
19
|
+
export interface SigningScheme {
|
|
20
|
+
readonly name: "sigv4" | "rfc9421";
|
|
21
|
+
/**
|
|
22
|
+
* Signs a request in place.
|
|
23
|
+
*
|
|
24
|
+
* `region` and `service` are ignored by {@link RFC9421}: SigV4 needs them to derive its signing
|
|
25
|
+
* key, whereas RFC 9421 signs the `x-euclid-region` and `x-euclid-target` headers that carry the
|
|
26
|
+
* same facts, and binding them twice would add a way for the two copies to disagree.
|
|
27
|
+
*/
|
|
28
|
+
sign(req: SignableRequest, accessKeyId: string, secretAccessKey: string, region: string, service: string): void;
|
|
29
|
+
/** Verifies a request signed with this scheme, answering the access key ID or null. */
|
|
30
|
+
verify(req: SignableRequest, lookupSecret: (keyId: string) => string | null | undefined): string | null;
|
|
31
|
+
/** The headers {@link SigningScheme.sign} writes, for callers that copy them onto another request. */
|
|
32
|
+
signatureHeaderNames(): readonly string[];
|
|
33
|
+
}
|
|
34
|
+
/** What euclid has always spoken, and the default. */
|
|
35
|
+
export declare const SIGV4: SigningScheme;
|
|
36
|
+
/** The standard scheme meant to replace SigV4. */
|
|
37
|
+
export declare const RFC9421: SigningScheme;
|
|
38
|
+
/**
|
|
39
|
+
* Which scheme, if either, a received request presents a signature for.
|
|
40
|
+
*
|
|
41
|
+
* A routing decision, not a verification: it says which {@link SigningScheme.verify} to call and
|
|
42
|
+
* nothing about whether that call will succeed. Null means the request presents no signature at
|
|
43
|
+
* all - a bearer-token request, or an unsigned one.
|
|
44
|
+
*/
|
|
45
|
+
export declare function signingSchemeOf(req: SignableRequest): SigningScheme | null;
|
|
46
|
+
//# sourceMappingURL=scheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheme.d.ts","sourceRoot":"","sources":["../../src/auth/scheme.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAG3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAEnC;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhH,uFAAuF;IACvF,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;IAExG,sGAAsG;IACtG,oBAAoB,IAAI,SAAS,MAAM,EAAE,CAAC;CAC3C;AAED,sDAAsD;AACtD,eAAO,MAAM,KAAK,EAAE,aAWlB,CAAC;AAEH,kDAAkD;AAClD,eAAO,MAAM,OAAO,EAAE,aAWpB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,aAAa,GAAG,IAAI,CAI1E"}
|