@motebit/crypto-tpm 1.0.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.
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Minimal TPM 2.0 binary-format parser.
3
+ *
4
+ * The TPM marshals structured data as big-endian size-prefixed bytes —
5
+ * distinct from CBOR / DER / JSON and wholly specific to the TCG's TPM
6
+ * Structures specification. Verification of a TPM quote requires
7
+ * extracting exactly three fields from the `TPMS_ATTEST` structure:
8
+ *
9
+ * - `magic` — 4-byte constant, must equal `TPM_GENERATED_VALUE`.
10
+ * - `type` — 2-byte constant, `TPM_ST_ATTEST_QUOTE` for quotes.
11
+ * - `qualifiedSigner` — length-prefixed `TPM2B_NAME`, identifies the AK.
12
+ * - `extraData` — length-prefixed `TPM2B_DATA`, this is where
13
+ * motebit threads the identity binding.
14
+ *
15
+ * We deliberately skip the remainder (clockInfo, firmwareVersion, the
16
+ * attested quote body containing PCR digests) — motebit's claim is
17
+ * about identity binding, not platform-state attestation. The body's
18
+ * signature (produced by TPM2_Quote using the AK) covers the entire
19
+ * serialized `TPMS_ATTEST`; any byte drift would invalidate it.
20
+ *
21
+ * Hand-rolled over `node-tpm2-pts`: the parser is ~80 LOC and covers
22
+ * exactly our needs. Pulling a dep for that would cross a larger
23
+ * attack surface than the struct we parse.
24
+ *
25
+ * References:
26
+ * - TCG TPM 2.0 Library, Part 2: Structures — §10.12 `TPMS_ATTEST`.
27
+ * - TCG TPM 2.0 Library, Part 1: Architecture — §36.1 `TPM2B_*` types.
28
+ */
29
+ /** Constant magic value every TPM-signed attest carries. */
30
+ export const TPM_GENERATED_VALUE = 0xff544347; // ASCII "ÿTCG"
31
+ /** `TPM_ST_ATTEST_QUOTE` — the structure-tag identifying a quote attestation. */
32
+ export const TPM_ST_ATTEST_QUOTE = 0x8018;
33
+ /**
34
+ * Parse a TPM 2.0 `TPMS_ATTEST` structure. Throws on malformed input;
35
+ * the outer `verify.ts` catches and converts to the fail-closed result
36
+ * shape.
37
+ *
38
+ * Wire layout:
39
+ * uint32 magic ; 4 bytes
40
+ * uint16 type ; 2 bytes
41
+ * TPM2B_NAME qualifiedSigner ; uint16 size || bytes
42
+ * TPM2B_DATA extraData ; uint16 size || bytes
43
+ * TPMS_CLOCK_INFO clockInfo ; 17 bytes (fixed)
44
+ * uint64 firmwareVersion ; 8 bytes
45
+ * TPMU_ATTEST attested ; variable, tag-specific
46
+ *
47
+ * We extract magic / type / qualifiedSigner / extraData and stash the
48
+ * rest in `trailer` so the caller can reconstruct the full signed
49
+ * bytes when verifying the AK signature. The caller retains the
50
+ * original buffer; this view points into it via length-accurate
51
+ * subarray copies.
52
+ */
53
+ export function parseTpmsAttest(bytes) {
54
+ if (bytes.length < 4 + 2 + 2 + 2) {
55
+ throw new Error(`TPMS_ATTEST too short: ${bytes.length} bytes (need at least 10 for header fields)`);
56
+ }
57
+ const reader = new TpmReader(bytes);
58
+ const magic = reader.readUint32();
59
+ const type = reader.readUint16();
60
+ const qualifiedSigner = reader.readTpm2B();
61
+ const extraData = reader.readTpm2B();
62
+ const trailer = reader.remaining();
63
+ return { magic, type, qualifiedSigner, extraData, trailer };
64
+ }
65
+ /**
66
+ * Build a minimal `TPMS_ATTEST` byte sequence for tests.
67
+ *
68
+ * Tests fabricate a valid-shape attest so the verifier's parsing +
69
+ * extraData-binding code paths exercise without a real TPM. Production
70
+ * minting lives in the Rust bridge — never call `composeTpmsAttest`
71
+ * outside `__tests__/` or you'll drift from what a real TPM would sign.
72
+ *
73
+ * `trailer` defaults to a deterministic 17-byte clockInfo + 8-byte
74
+ * firmwareVersion filler + 4-byte quote filler so the resulting bytes
75
+ * pass the minimum-length check during parsing; tests that need the
76
+ * real trailing fields can override.
77
+ */
78
+ export function composeTpmsAttestForTest(input) {
79
+ const magic = input.magic ?? TPM_GENERATED_VALUE;
80
+ const type = input.type ?? TPM_ST_ATTEST_QUOTE;
81
+ const trailer = input.trailer ?? new Uint8Array(17 + 8 + 4);
82
+ const out = new TpmWriter();
83
+ out.writeUint32(magic);
84
+ out.writeUint16(type);
85
+ out.writeTpm2B(input.qualifiedSigner);
86
+ out.writeTpm2B(input.extraData);
87
+ out.writeRaw(trailer);
88
+ return out.toBytes();
89
+ }
90
+ // ── Internal helpers ────────────────────────────────────────────────
91
+ class TpmReader {
92
+ bytes;
93
+ offset = 0;
94
+ constructor(bytes) {
95
+ this.bytes = bytes;
96
+ }
97
+ readUint16() {
98
+ if (this.offset + 2 > this.bytes.length) {
99
+ throw new Error(`TPM reader: uint16 at offset ${this.offset} would overrun buffer`);
100
+ }
101
+ const hi = this.bytes[this.offset];
102
+ const lo = this.bytes[this.offset + 1];
103
+ this.offset += 2;
104
+ return (hi << 8) | lo;
105
+ }
106
+ readUint32() {
107
+ if (this.offset + 4 > this.bytes.length) {
108
+ throw new Error(`TPM reader: uint32 at offset ${this.offset} would overrun buffer`);
109
+ }
110
+ const b0 = this.bytes[this.offset];
111
+ const b1 = this.bytes[this.offset + 1];
112
+ const b2 = this.bytes[this.offset + 2];
113
+ const b3 = this.bytes[this.offset + 3];
114
+ this.offset += 4;
115
+ // Use unsigned-right-shift by 0 to force a non-negative 32-bit value.
116
+ return ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) >>> 0;
117
+ }
118
+ readTpm2B() {
119
+ const size = this.readUint16();
120
+ if (this.offset + size > this.bytes.length) {
121
+ throw new Error(`TPM reader: TPM2B length ${size} at offset ${this.offset} would overrun buffer ` +
122
+ `(length ${this.bytes.length})`);
123
+ }
124
+ const value = this.bytes.subarray(this.offset, this.offset + size);
125
+ this.offset += size;
126
+ // Return a detached copy — consumers may treat the result as owned.
127
+ return new Uint8Array(value);
128
+ }
129
+ remaining() {
130
+ const rest = this.bytes.subarray(this.offset);
131
+ return new Uint8Array(rest);
132
+ }
133
+ }
134
+ class TpmWriter {
135
+ chunks = [];
136
+ writeUint16(v) {
137
+ const buf = new Uint8Array(2);
138
+ buf[0] = (v >> 8) & 0xff;
139
+ buf[1] = v & 0xff;
140
+ this.chunks.push(buf);
141
+ }
142
+ writeUint32(v) {
143
+ const buf = new Uint8Array(4);
144
+ buf[0] = (v >>> 24) & 0xff;
145
+ buf[1] = (v >>> 16) & 0xff;
146
+ buf[2] = (v >>> 8) & 0xff;
147
+ buf[3] = v & 0xff;
148
+ this.chunks.push(buf);
149
+ }
150
+ writeTpm2B(bytes) {
151
+ this.writeUint16(bytes.length);
152
+ this.chunks.push(new Uint8Array(bytes));
153
+ }
154
+ writeRaw(bytes) {
155
+ this.chunks.push(new Uint8Array(bytes));
156
+ }
157
+ toBytes() {
158
+ let total = 0;
159
+ for (const c of this.chunks)
160
+ total += c.length;
161
+ const out = new Uint8Array(total);
162
+ let at = 0;
163
+ for (const c of this.chunks) {
164
+ out.set(c, at);
165
+ at += c.length;
166
+ }
167
+ return out;
168
+ }
169
+ }
170
+ //# sourceMappingURL=tpm-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tpm-parse.js","sourceRoot":"","sources":["../src/tpm-parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC,CAAC,eAAe;AAE9D,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAqB1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,0BAA0B,KAAK,CAAC,MAAM,6CAA6C,CACpF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACjC,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAEnC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAMxC;IACC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACjD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,mBAAmB,CAAC;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,IAAI,UAAU,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAE5D,MAAM,GAAG,GAAG,IAAI,SAAS,EAAE,CAAC;IAC5B,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACvB,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACtC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACtB,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;AACvB,CAAC;AAED,uEAAuE;AAEvE,MAAM,SAAS;IAEgB;IADrB,MAAM,GAAG,CAAC,CAAC;IACnB,YAA6B,KAAiB;QAAjB,UAAK,GAAL,KAAK,CAAY;IAAG,CAAC;IAElD,UAAU;QACR,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,MAAM,uBAAuB,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAE,CAAC;QACpC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,MAAM,uBAAuB,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAE,CAAC;QACpC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACjB,sEAAsE;QACtE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,cAAc,IAAI,CAAC,MAAM,wBAAwB;gBAC/E,WAAW,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAClC,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;QACpB,oEAAoE;QACpE,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,SAAS;IACL,MAAM,GAAiB,EAAE,CAAC;IAElC,WAAW,CAAC,CAAS;QACnB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACzB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,WAAW,CAAC,CAAS;QACnB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QAC1B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,UAAU,CAAC,KAAiB;QAC1B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,QAAQ,CAAC,KAAiB;QACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO;QACL,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;QAC/C,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACf,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC;QACjB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Pinned TPM 2.0 Endorsement-Key vendor root certificates.
3
+ *
4
+ * Every `platform: "tpm"` hardware-attestation claim must chain to one of
5
+ * the root CAs listed here. Pinning is the self-attesting contract — a
6
+ * verifier that dynamically fetched vendor CAs could not be reproduced
7
+ * by a third-party audit. By committing the exact bytes of the CAs we
8
+ * accept, anyone can audit this file, pin the same bytes in their own
9
+ * verifier, and reach the same yes/no answer.
10
+ *
11
+ * The TPM ecosystem is multi-vendor by design: every Windows 11 device
12
+ * ships with TPM 2.0 (Microsoft's mandatory requirement), every modern
13
+ * Linux-on-x86 laptop has one, every Mac with a T2 chip has one. Each
14
+ * vendor maintains a public CA bundle rooted at their Endorsement-Key
15
+ * issuer. The motebit policy pins the four most common:
16
+ *
17
+ * - Infineon (`OptigaTrustM`, `SLB966x` families)
18
+ * - Nuvoton (`NPCT7xx` family)
19
+ * - STMicroelectronics (`ST33TPHF2ESPI`, `ST33HTPH2E32AHB3` families)
20
+ * - Intel PTT (firmware TPM bundled with Intel CSME)
21
+ *
22
+ * AMD fTPM (firmware TPM bundled with AMD PSP) uses a vendor-signed
23
+ * chain that roots to AMD's EK CA; that root is additive and lands in
24
+ * a subsequent pass once the first AMD-shaped test vector is captured.
25
+ *
26
+ * ## Operator follow-up — ship-blocking for production rollout
27
+ *
28
+ * The PEMs below are declared as exported constants so the test suite
29
+ * exercises the same chain-verification code path end-to-end. For a
30
+ * production ship, an operator must replace each placeholder with the
31
+ * exact byte-for-byte vendor root published at the URL in the comment.
32
+ * The test fabrication pattern (`buildFakeChain` in `__tests__`) does
33
+ * not need the real bytes — tests inject their own roots — so swapping
34
+ * in the real vendor PEMs is a mechanical operator task, not a code
35
+ * change. The drift gate `check-hardware-attestation-primitives` covers
36
+ * the parser / composer contract; the vendor-root swap is tracked in
37
+ * `docs/doctrine/hardware-attestation.md` §Non-goals.
38
+ */
39
+ /**
40
+ * Infineon OPTIGA TPM 2.0 Endorsement Key Root CA.
41
+ *
42
+ * Published at: https://pki.infineon.com/OptigaEccRootCA/OptigaEccRootCA.crt
43
+ *
44
+ * Placeholder PEM — replace with the real vendor bytes before
45
+ * production rollout. Tests override via `rootPems` option.
46
+ */
47
+ export declare const INFINEON_TPM_EK_ROOT_PEM = "-----BEGIN CERTIFICATE-----\nMIIBdjCCARygAwIBAgIJAIMw8f7k8+xyMAoGCCqGSM49BAMCMCIxIDAeBgNVBAMM\nF01vdGViaXQgSW5maW5lb24gUGxhY2Vob2xkZXIwHhcNMjYwNDIyMDAwMDAwWhcN\nNDYwNDIyMDAwMDAwWjAiMSAwHgYDVQQDDBdNb3RlYml0IEluZmluZW9uIFBsYWNl\naG9sZGVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKZ/4LNYqi/LAI4R6tS2K\nkRUnhkRzkYfi5hmz2E+35mqWVNqCb/FRhk6dEuxCNbwJxFPEK4Opf5lCOs0ZsRdF\n+KNCMEAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQp3ojpUGm1YB9N+9lQHg0s\nVpSoBTAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCjxfFCCf6t\nCpGcGc7Gsk8h2RFQ7CFW8NzkjuvUZZ7bwwIhAJ/CB4+XzV5EhcOf0qRZN8zmJb8G\nB9Z9EFcZ7Nt1l4Tn\n-----END CERTIFICATE-----\n";
48
+ /**
49
+ * Nuvoton NPCT TPM 2.0 Endorsement Key Root CA.
50
+ *
51
+ * Published at: https://www.nuvoton.com/security/NTC-TPM-EK-Cert/Nuvoton TPM Root CA 2110.cer
52
+ *
53
+ * Placeholder PEM — replace with the real vendor bytes before
54
+ * production rollout. Tests override via `rootPems` option.
55
+ */
56
+ export declare const NUVOTON_TPM_EK_ROOT_PEM = "-----BEGIN CERTIFICATE-----\nMIIBczCCARmgAwIBAgIJAL7X6p2yXxJJMAoGCCqGSM49BAMCMCAxHjAcBgNVBAMM\nFU1vdGViaXQgTnV2b3RvbiBQbGFjZWhvbGRlcjAeFw0yNjA0MjIwMDAwMDBaFw00\nNjA0MjIwMDAwMDBaMCAxHjAcBgNVBAMMFU1vdGViaXQgTnV2b3RvbiBQbGFjZWhv\nbGRlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJgPwHoU+cVjX3HzkXpEksdn\nf3KPRwMbFYvE3tkqDcW8JqzG8qO5VwPKFPwoAEE2C8dJpKHEk7fA4iGrSXz7x/6j\nQjBAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUNskUYy3Uz8Tvuvbu/B5VTJA2\nlLcwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiAAbU6/dL06n8Cw\nCYI/rHo/cLWEFQKH5VnzDJH4RN5fIgIgAN0F3fYbTBa9H8OXCJdXUDxSDr2iT8E5\nVDz6f2s3uFo=\n-----END CERTIFICATE-----\n";
57
+ /**
58
+ * STMicroelectronics ST33 TPM 2.0 Endorsement Key Root CA.
59
+ *
60
+ * Published at: https://sw-center.st.com/STM_ROOT_CA_2.crt
61
+ *
62
+ * Placeholder PEM — replace with the real vendor bytes before
63
+ * production rollout. Tests override via `rootPems` option.
64
+ */
65
+ export declare const STMICRO_TPM_EK_ROOT_PEM = "-----BEGIN CERTIFICATE-----\nMIIBdDCCARqgAwIBAgIJAMZ4+9xZHMuaMAoGCCqGSM49BAMCMCExHzAdBgNVBAMM\nFk1vdGViaXQgU1RNaWNybyBQbGFjZWhvbGRlcjAeFw0yNjA0MjIwMDAwMDBaFw00\nNjA0MjIwMDAwMDBaMCExHzAdBgNVBAMMFk1vdGViaXQgU1RNaWNybyBQbGFjZWhv\nbGRlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLnK3t7y4JBJxzE0EFq+zsOV\n+m9n9D1YDUFb7k6hVIsKvfoH9o3rZkc4uRuSsz7fjC+IsKsMrJKXaU0mxH6ncjej\nQjBAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUe/x8wdJYzEypFT3M0K1Jy5C6\n1j8wDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiAc9Ln4qhL7fZ5c\noUbLFsTVTEc4aeBMkxqzLrJpZOYVegIgWSfLj2Q5CQ8OFvJx8fVDkxN9OXjYT6Jm\nH6Bvb0gQaG8=\n-----END CERTIFICATE-----\n";
66
+ /**
67
+ * Intel PTT (Platform Trust Technology, firmware TPM inside Intel CSME)
68
+ * Endorsement Key Root CA.
69
+ *
70
+ * Published at: https://upgrades.intel.com/content/CRL/ekcert/EKRootPublicKey.cer
71
+ *
72
+ * Placeholder PEM — replace with the real vendor bytes before
73
+ * production rollout. Tests override via `rootPems` option.
74
+ */
75
+ export declare const INTEL_PTT_EK_ROOT_PEM = "-----BEGIN CERTIFICATE-----\nMIIBcjCCARegAwIBAgIJAOQz8pPRrTIxMAoGCCqGSM49BAMCMB8xHTAbBgNVBAMM\nFE1vdGViaXQgSW50ZWwgUGxhY2Vob2xkZXIwHhcNMjYwNDIyMDAwMDAwWhcNNDYw\nNDIyMDAwMDAwWjAfMR0wGwYDVQQDDBRNb3RlYml0IEludGVsIFBsYWNlaG9sZGVy\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkxD3N3JQMgVV8gRZEiQLBPyxX5jw\nWHNJCt8Fc0BbzQZVZ6Vkg4J1oHkLXIpsWcNOwU1RXcE/Pzr2yIjTnJW2VKNCMEAw\nDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQxu9vHJmf+rQznfCVCd9vNQTRwPjAP\nBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQCbX9rmZqJgk7lYPXGj\nWBR+oXt4AYzQ8pQvTSfkG/DBYwIgEY/oKZl5QL3Jt7lJx6lJxF3vLkaKBnJ9t4K4\ngHQ4nCY=\n-----END CERTIFICATE-----\n";
76
+ /**
77
+ * Default pinned-root set returned when a caller passes no `rootPems`
78
+ * override. Ordered by deployment prevalence — Infineon and Intel PTT
79
+ * together cover the vast majority of Windows 11 hosts; Nuvoton and
80
+ * STMicro cover most non-Intel Linux laptops.
81
+ */
82
+ export declare const DEFAULT_PINNED_TPM_ROOTS: readonly string[];
83
+ /**
84
+ * Sentinel value the consumer-facing verifier emits on a mint path
85
+ * where the Rust bridge returns a `not_supported` failure envelope.
86
+ * Exposed as a constant so call sites match on a named token rather
87
+ * than a raw string literal.
88
+ */
89
+ export declare const TPM_PLATFORM: "tpm";
90
+ //# sourceMappingURL=tpm-roots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tpm-roots.d.ts","sourceRoot":"","sources":["../src/tpm-roots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,+lBAWpC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,2lBAWnC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,2lBAWnC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,ulBAWjC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAKrD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAG,KAAc,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Pinned TPM 2.0 Endorsement-Key vendor root certificates.
3
+ *
4
+ * Every `platform: "tpm"` hardware-attestation claim must chain to one of
5
+ * the root CAs listed here. Pinning is the self-attesting contract — a
6
+ * verifier that dynamically fetched vendor CAs could not be reproduced
7
+ * by a third-party audit. By committing the exact bytes of the CAs we
8
+ * accept, anyone can audit this file, pin the same bytes in their own
9
+ * verifier, and reach the same yes/no answer.
10
+ *
11
+ * The TPM ecosystem is multi-vendor by design: every Windows 11 device
12
+ * ships with TPM 2.0 (Microsoft's mandatory requirement), every modern
13
+ * Linux-on-x86 laptop has one, every Mac with a T2 chip has one. Each
14
+ * vendor maintains a public CA bundle rooted at their Endorsement-Key
15
+ * issuer. The motebit policy pins the four most common:
16
+ *
17
+ * - Infineon (`OptigaTrustM`, `SLB966x` families)
18
+ * - Nuvoton (`NPCT7xx` family)
19
+ * - STMicroelectronics (`ST33TPHF2ESPI`, `ST33HTPH2E32AHB3` families)
20
+ * - Intel PTT (firmware TPM bundled with Intel CSME)
21
+ *
22
+ * AMD fTPM (firmware TPM bundled with AMD PSP) uses a vendor-signed
23
+ * chain that roots to AMD's EK CA; that root is additive and lands in
24
+ * a subsequent pass once the first AMD-shaped test vector is captured.
25
+ *
26
+ * ## Operator follow-up — ship-blocking for production rollout
27
+ *
28
+ * The PEMs below are declared as exported constants so the test suite
29
+ * exercises the same chain-verification code path end-to-end. For a
30
+ * production ship, an operator must replace each placeholder with the
31
+ * exact byte-for-byte vendor root published at the URL in the comment.
32
+ * The test fabrication pattern (`buildFakeChain` in `__tests__`) does
33
+ * not need the real bytes — tests inject their own roots — so swapping
34
+ * in the real vendor PEMs is a mechanical operator task, not a code
35
+ * change. The drift gate `check-hardware-attestation-primitives` covers
36
+ * the parser / composer contract; the vendor-root swap is tracked in
37
+ * `docs/doctrine/hardware-attestation.md` §Non-goals.
38
+ */
39
+ /**
40
+ * Infineon OPTIGA TPM 2.0 Endorsement Key Root CA.
41
+ *
42
+ * Published at: https://pki.infineon.com/OptigaEccRootCA/OptigaEccRootCA.crt
43
+ *
44
+ * Placeholder PEM — replace with the real vendor bytes before
45
+ * production rollout. Tests override via `rootPems` option.
46
+ */
47
+ export const INFINEON_TPM_EK_ROOT_PEM = `-----BEGIN CERTIFICATE-----
48
+ MIIBdjCCARygAwIBAgIJAIMw8f7k8+xyMAoGCCqGSM49BAMCMCIxIDAeBgNVBAMM
49
+ F01vdGViaXQgSW5maW5lb24gUGxhY2Vob2xkZXIwHhcNMjYwNDIyMDAwMDAwWhcN
50
+ NDYwNDIyMDAwMDAwWjAiMSAwHgYDVQQDDBdNb3RlYml0IEluZmluZW9uIFBsYWNl
51
+ aG9sZGVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKZ/4LNYqi/LAI4R6tS2K
52
+ kRUnhkRzkYfi5hmz2E+35mqWVNqCb/FRhk6dEuxCNbwJxFPEK4Opf5lCOs0ZsRdF
53
+ +KNCMEAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQp3ojpUGm1YB9N+9lQHg0s
54
+ VpSoBTAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCjxfFCCf6t
55
+ CpGcGc7Gsk8h2RFQ7CFW8NzkjuvUZZ7bwwIhAJ/CB4+XzV5EhcOf0qRZN8zmJb8G
56
+ B9Z9EFcZ7Nt1l4Tn
57
+ -----END CERTIFICATE-----
58
+ `;
59
+ /**
60
+ * Nuvoton NPCT TPM 2.0 Endorsement Key Root CA.
61
+ *
62
+ * Published at: https://www.nuvoton.com/security/NTC-TPM-EK-Cert/Nuvoton TPM Root CA 2110.cer
63
+ *
64
+ * Placeholder PEM — replace with the real vendor bytes before
65
+ * production rollout. Tests override via `rootPems` option.
66
+ */
67
+ export const NUVOTON_TPM_EK_ROOT_PEM = `-----BEGIN CERTIFICATE-----
68
+ MIIBczCCARmgAwIBAgIJAL7X6p2yXxJJMAoGCCqGSM49BAMCMCAxHjAcBgNVBAMM
69
+ FU1vdGViaXQgTnV2b3RvbiBQbGFjZWhvbGRlcjAeFw0yNjA0MjIwMDAwMDBaFw00
70
+ NjA0MjIwMDAwMDBaMCAxHjAcBgNVBAMMFU1vdGViaXQgTnV2b3RvbiBQbGFjZWhv
71
+ bGRlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJgPwHoU+cVjX3HzkXpEksdn
72
+ f3KPRwMbFYvE3tkqDcW8JqzG8qO5VwPKFPwoAEE2C8dJpKHEk7fA4iGrSXz7x/6j
73
+ QjBAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUNskUYy3Uz8Tvuvbu/B5VTJA2
74
+ lLcwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiAAbU6/dL06n8Cw
75
+ CYI/rHo/cLWEFQKH5VnzDJH4RN5fIgIgAN0F3fYbTBa9H8OXCJdXUDxSDr2iT8E5
76
+ VDz6f2s3uFo=
77
+ -----END CERTIFICATE-----
78
+ `;
79
+ /**
80
+ * STMicroelectronics ST33 TPM 2.0 Endorsement Key Root CA.
81
+ *
82
+ * Published at: https://sw-center.st.com/STM_ROOT_CA_2.crt
83
+ *
84
+ * Placeholder PEM — replace with the real vendor bytes before
85
+ * production rollout. Tests override via `rootPems` option.
86
+ */
87
+ export const STMICRO_TPM_EK_ROOT_PEM = `-----BEGIN CERTIFICATE-----
88
+ MIIBdDCCARqgAwIBAgIJAMZ4+9xZHMuaMAoGCCqGSM49BAMCMCExHzAdBgNVBAMM
89
+ Fk1vdGViaXQgU1RNaWNybyBQbGFjZWhvbGRlcjAeFw0yNjA0MjIwMDAwMDBaFw00
90
+ NjA0MjIwMDAwMDBaMCExHzAdBgNVBAMMFk1vdGViaXQgU1RNaWNybyBQbGFjZWhv
91
+ bGRlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLnK3t7y4JBJxzE0EFq+zsOV
92
+ +m9n9D1YDUFb7k6hVIsKvfoH9o3rZkc4uRuSsz7fjC+IsKsMrJKXaU0mxH6ncjej
93
+ QjBAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUe/x8wdJYzEypFT3M0K1Jy5C6
94
+ 1j8wDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiAc9Ln4qhL7fZ5c
95
+ oUbLFsTVTEc4aeBMkxqzLrJpZOYVegIgWSfLj2Q5CQ8OFvJx8fVDkxN9OXjYT6Jm
96
+ H6Bvb0gQaG8=
97
+ -----END CERTIFICATE-----
98
+ `;
99
+ /**
100
+ * Intel PTT (Platform Trust Technology, firmware TPM inside Intel CSME)
101
+ * Endorsement Key Root CA.
102
+ *
103
+ * Published at: https://upgrades.intel.com/content/CRL/ekcert/EKRootPublicKey.cer
104
+ *
105
+ * Placeholder PEM — replace with the real vendor bytes before
106
+ * production rollout. Tests override via `rootPems` option.
107
+ */
108
+ export const INTEL_PTT_EK_ROOT_PEM = `-----BEGIN CERTIFICATE-----
109
+ MIIBcjCCARegAwIBAgIJAOQz8pPRrTIxMAoGCCqGSM49BAMCMB8xHTAbBgNVBAMM
110
+ FE1vdGViaXQgSW50ZWwgUGxhY2Vob2xkZXIwHhcNMjYwNDIyMDAwMDAwWhcNNDYw
111
+ NDIyMDAwMDAwWjAfMR0wGwYDVQQDDBRNb3RlYml0IEludGVsIFBsYWNlaG9sZGVy
112
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkxD3N3JQMgVV8gRZEiQLBPyxX5jw
113
+ WHNJCt8Fc0BbzQZVZ6Vkg4J1oHkLXIpsWcNOwU1RXcE/Pzr2yIjTnJW2VKNCMEAw
114
+ DgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQxu9vHJmf+rQznfCVCd9vNQTRwPjAP
115
+ BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQCbX9rmZqJgk7lYPXGj
116
+ WBR+oXt4AYzQ8pQvTSfkG/DBYwIgEY/oKZl5QL3Jt7lJx6lJxF3vLkaKBnJ9t4K4
117
+ gHQ4nCY=
118
+ -----END CERTIFICATE-----
119
+ `;
120
+ /**
121
+ * Default pinned-root set returned when a caller passes no `rootPems`
122
+ * override. Ordered by deployment prevalence — Infineon and Intel PTT
123
+ * together cover the vast majority of Windows 11 hosts; Nuvoton and
124
+ * STMicro cover most non-Intel Linux laptops.
125
+ */
126
+ export const DEFAULT_PINNED_TPM_ROOTS = [
127
+ INFINEON_TPM_EK_ROOT_PEM,
128
+ NUVOTON_TPM_EK_ROOT_PEM,
129
+ STMICRO_TPM_EK_ROOT_PEM,
130
+ INTEL_PTT_EK_ROOT_PEM,
131
+ ];
132
+ /**
133
+ * Sentinel value the consumer-facing verifier emits on a mint path
134
+ * where the Rust bridge returns a `not_supported` failure envelope.
135
+ * Exposed as a constant so call sites match on a named token rather
136
+ * than a raw string literal.
137
+ */
138
+ export const TPM_PLATFORM = "tpm";
139
+ //# sourceMappingURL=tpm-roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tpm-roots.js","sourceRoot":"","sources":["../src/tpm-roots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;;CAWvC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;CAWtC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;CAWtC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;CAWpC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,wBAAwB;IACxB,uBAAuB;IACvB,uBAAuB;IACvB,qBAAqB;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAc,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * TPM 2.0 quote verifier — the core judgment function this package
3
+ * exports.
4
+ *
5
+ * Flow (matches the TCG verification recipe for TPM2_Quote, plus the
6
+ * motebit-specific identity-key binding step):
7
+ *
8
+ * 1. Split the receipt into (tpmsAttestBase64, signatureBase64,
9
+ * akCertDerBase64, intermediateCertsDerBase64Joined).
10
+ * 2. Parse the TPMS_ATTEST bytes. Assert magic = TPM_GENERATED_VALUE
11
+ * and type = TPM_ST_ATTEST_QUOTE.
12
+ * 3. Parse the AK leaf + any intermediates as X.509. Walk the chain
13
+ * from AK leaf → intermediate → vendor root. Every non-leaf must
14
+ * carry `basicConstraints.cA === true`. Every signature must
15
+ * verify under its issuer's public key. Every validity window
16
+ * must include `now`. The terminal cert's DER must byte-equal
17
+ * ONE of the pinned vendor roots.
18
+ * 4. Verify the AK signature over SHA-256(TPMS_ATTEST_BYTES) using
19
+ * the AK certificate's public key.
20
+ * 5. Re-derive `extraData` from the JCS-canonical body
21
+ * {attested_at, device_id, identity_public_key, motebit_id,
22
+ * platform: "tpm", version: "1"} — SHA-256 of the canonical body
23
+ * — and byte-compare against the transmitted `extraData`. This is
24
+ * the cross-stack binding — without it every other step would
25
+ * prove only that *some* TPM-enrolled device did something, not
26
+ * that the Ed25519 key the credential subject claims is bound
27
+ * to that device.
28
+ *
29
+ * The TPM's own EK certificate provisioning path is NOT verified here
30
+ * — that would require contacting the vendor's EK provisioning service
31
+ * and is out of scope for v1. The outer chain + extraData binding is
32
+ * enough for third-party self-verification of TPM-attested identity.
33
+ */
34
+ import type { HardwareAttestationClaim } from "@motebit/protocol";
35
+ export interface TpmVerifyOptions {
36
+ /**
37
+ * Ed25519 identity key (lowercase hex) the motebit VC claims. The
38
+ * TPM quote's extraData MUST bind this key.
39
+ */
40
+ readonly expectedIdentityPublicKeyHex: string;
41
+ /**
42
+ * motebit_id from the credential subject. Participates in the JCS
43
+ * body the Rust bridge hashes into extraData; re-derived here and
44
+ * byte-compared against the transmitted extraData so a malicious
45
+ * native client cannot substitute a different body.
46
+ */
47
+ readonly expectedMotebitId?: string;
48
+ /**
49
+ * device_id from the credential subject. Same binding role as
50
+ * `expectedMotebitId`.
51
+ */
52
+ readonly expectedDeviceId?: string;
53
+ /**
54
+ * `attested_at` (unix ms) from the credential subject. Same binding
55
+ * role as `expectedMotebitId`.
56
+ */
57
+ readonly expectedAttestedAt?: number;
58
+ /**
59
+ * Override the pinned vendor roots. Tests fabricate their own root
60
+ * so chain verification exercises the same code path without needing
61
+ * real vendor-signed leaves. Defaults to `DEFAULT_PINNED_TPM_ROOTS`.
62
+ */
63
+ readonly rootPems?: readonly string[];
64
+ /**
65
+ * Clock for chain-validity checks. Defaults to `Date.now`. Tests
66
+ * inject a fixed clock to keep certificate validity windows
67
+ * deterministic.
68
+ */
69
+ readonly now?: () => number;
70
+ }
71
+ export interface TpmVerifyError {
72
+ readonly message: string;
73
+ }
74
+ export interface TpmVerifyResult {
75
+ readonly valid: boolean;
76
+ readonly cert_chain_valid: boolean;
77
+ readonly quote_signature_valid: boolean;
78
+ readonly quote_shape_valid: boolean;
79
+ readonly identity_bound: boolean;
80
+ readonly errors: readonly TpmVerifyError[];
81
+ }
82
+ /**
83
+ * TPM 2.0 quote verifier.
84
+ *
85
+ * Pure. No network. No filesystem. Deterministic given `now()`.
86
+ *
87
+ * `claim` is the `HardwareAttestationClaim` as carried inside the
88
+ * motebit AgentTrustCredential. For TPM, the `attestation_receipt`
89
+ * field is expected to be four base64url segments separated by `.`:
90
+ *
91
+ * `{tpmsAttestB64}.{signatureB64}.{akCertDerB64}.{intermediatesJoinedB64}`
92
+ *
93
+ * `intermediatesJoinedB64` may itself be an empty segment (`""`) when
94
+ * the AK cert chains directly to a pinned root, or a `,`-joined
95
+ * concatenation of base64url-encoded DER intermediates in leaf-
96
+ * proximal-first order otherwise.
97
+ */
98
+ export declare function verifyTpmQuote(claim: HardwareAttestationClaim, opts: TpmVerifyOptions): Promise<TpmVerifyResult>;
99
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAKlE,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,4BAA4B,EAAE,MAAM,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;CAC5C;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CA0M1B"}