node-tpm2 0.0.4-beta.3 → 0.0.5-beta.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,1029 @@
1
+ # node-tpm2 API Reference
2
+
3
+ Complete reference for the public JavaScript API of **node-tpm2** — native TPM 2.0 bindings for Node.js. The library talks to the TPM through OS-native transports (TBS on Windows, `/dev/tpmrm0` on Linux) and returns Buffers and typed records, not CLI text.
4
+
5
+ **Import:**
6
+
7
+ ```javascript
8
+ import { Tpm, TpmError } from 'node-tpm2';
9
+ ```
10
+
11
+ **Related docs:** [Getting started](./getting-started.md) · [Windows PCP / fleet enrollment](./windows-pcp.md) · [Roadmap](./roadmap.md)
12
+
13
+ ---
14
+
15
+ ## Table of contents
16
+
17
+ 1. [Architecture](#architecture)
18
+ 2. [Package structure](#package-structure)
19
+ 3. [Connection lifecycle](#connection-lifecycle)
20
+ 4. [Namespace vs flat API](#namespace-vs-flat-api)
21
+ 5. [Types and wire formats](#types-and-wire-formats)
22
+ 6. [Root API (`Tpm`)](#root-api-tpm)
23
+ 7. [Handle API (`TpmHandle`)](#handle-api-tpmhandle)
24
+ 8. [Key handles (`KeyHandle`)](#key-handles-keyhandle)
25
+ 9. [Attestation handles (`AkHandle`)](#attestation-handles-akhandle)
26
+ 10. [Attestation flows](#attestation-flows)
27
+ 11. [Error model (`TpmError`)](#error-model-tpmerror)
28
+ 12. [Platform differences](#platform-differences)
29
+ 13. [Privilege matrix](#privilege-matrix)
30
+ 14. [Deferred / not in public API](#deferred--not-in-public-api)
31
+ 15. [Symbol index](#symbol-index)
32
+
33
+ ---
34
+
35
+ ## Architecture
36
+
37
+ node-tpm2 is a three-layer stack: JavaScript API (`api.js`) → N-API native addon (`native.cjs`, Rust `src/napi.rs`) → TPM transport (`src/tbs/`).
38
+
39
+ ```mermaid
40
+ flowchart TB
41
+ subgraph js [JavaScript]
42
+ Tpm["Tpm / TpmHandle"]
43
+ Handles["KeyHandle / AkHandle"]
44
+ Tpm --> Handles
45
+ end
46
+
47
+ subgraph native [Native addon]
48
+ NAPI["napi.rs"]
49
+ TBS["tbs/ command codec"]
50
+ PCP["pcp.rs (Windows AK only)"]
51
+ NAPI --> TBS
52
+ NAPI --> PCP
53
+ end
54
+
55
+ subgraph os [OS transport]
56
+ Linux["/dev/tpmrm0"]
57
+ WinTBS["TBS Submit_Command"]
58
+ WinNCrypt["NCrypt PCP"]
59
+ end
60
+
61
+ Tpm --> NAPI
62
+ TBS --> Linux
63
+ TBS --> WinTBS
64
+ PCP --> WinNCrypt
65
+ ```
66
+
67
+ **Design rules:**
68
+
69
+ - **General keys, PCR, random, ReadPublic, NV, seal** use the shared TBS command path on both Linux and Windows so blobs and behavior stay aligned.
70
+ - **Attestation key persistence on Windows** uses NCrypt Platform Crypto Provider (PCP) because raw TBS cannot persist cross-user identity keys reliably.
71
+ - **Transient TPM handles** are created and flushed inside each native call. You do not manage TPM handle slots in JavaScript.
72
+
73
+ ---
74
+
75
+ ## Package structure
76
+
77
+ For npm consumers:
78
+
79
+ | Artifact | Role |
80
+ |----------|------|
81
+ | `node-tpm2` (this package) | Pure JS entry: `index.js`, `api.js`, `index.d.ts` |
82
+ | `native.cjs` | N-API addon loaded at runtime (may be missing until build/install) |
83
+ | `native.d.ts` | Auto-generated types for native bindings (internal; not re-exported) |
84
+ | `node-tpm2-<platform>-<arch>-<libc>` | Optional dependency with prebuilt `.node` binary |
85
+
86
+ Install resolves the correct platform package automatically. Node **20+** required. macOS installs succeed but `Tpm.isAvailable()` returns `false` — there is no Apple TPM backend.
87
+
88
+ You only import from `'node-tpm2'`. Do not import `native.cjs` directly.
89
+
90
+ ---
91
+
92
+ ## Connection lifecycle
93
+
94
+ ### Probe without opening
95
+
96
+ ```javascript
97
+ if (!(await Tpm.isAvailable())) {
98
+ // No TPM, no native binary, macOS, or permission denied at probe time
99
+ }
100
+ ```
101
+
102
+ `isAvailable()` never throws. It returns `false` if the native module is missing, the platform is unsupported, or the TPM is unreachable.
103
+
104
+ ### Open a session handle
105
+
106
+ ```javascript
107
+ await using tpm = await Tpm.open();
108
+ // ... use tpm.pcr, tpm.keys, tpm.attest, etc.
109
+ // Symbol.asyncDispose runs at block exit (Node 20+ explicit resource management)
110
+ ```
111
+
112
+ **What `Tpm.open()` does:**
113
+
114
+ 1. Verifies the native backend loaded (`requireNative('isAvailable')`).
115
+ 2. Calls `isAvailable()`; if false, throws `TpmError` with code `TPM_UNAVAILABLE`.
116
+ 3. Returns a **stateless** `TpmHandle` object — a namespace grouping API, not an open file descriptor.
117
+
118
+ **Disposal:** `[Symbol.asyncDispose]()` on `TpmHandle` is currently a no-op. Each operation opens a TBS context (or PCP session), performs work, and flushes transient TPM objects internally. Future versions may reuse a per-handle TBS context; disposal would then release it.
119
+
120
+ ### Info without opening
121
+
122
+ ```javascript
123
+ const info = await Tpm.getFixedProperties();
124
+ // or
125
+ const info = await Tpm.info(); // alias
126
+ ```
127
+
128
+ These call `TPM2_GetCapability` for fixed TPM properties and do not require `Tpm.open()`.
129
+
130
+ ---
131
+
132
+ ## Namespace vs flat API
133
+
134
+ Every implemented operation exists in two forms:
135
+
136
+ | Style | Example | When to use |
137
+ |-------|---------|-------------|
138
+ | **Namespace (preferred)** | `await using tpm = await Tpm.open(); await tpm.pcr.read([0])` | New code; groups related operations; returns rich handles for keys/AK |
139
+ | **Flat (legacy-compatible)** | `await Tpm.pcrRead([0])` | Scripts, one-shot calls, README examples |
140
+
141
+ Flat methods on `Tpm` call the same native functions as the namespace methods. Differences:
142
+
143
+ - `Tpm.provisionAk()` returns `{ akPublicDer, akBlob }` — not an `AkHandle`.
144
+ - `Tpm.createKey()` returns `{ publicKeyDer, keyBlob }` — not a `KeyHandle`.
145
+ - `tpm.keys.create()` / `tpm.attest.provisionAk()` return handles with methods (`sign`, `quote`, `export`, …).
146
+
147
+ See [Namespace comparison table](#namespace-comparison-table).
148
+
149
+ ---
150
+
151
+ ## Types and wire formats
152
+
153
+ ### `AkBlob` / `KeyBlob`
154
+
155
+ ```typescript
156
+ type AkBlob = { public: Buffer; private: Buffer };
157
+ type KeyBlob = AkBlob; // same wire shape; distinct type name for clarity
158
+ ```
159
+
160
+ | Field | Contents |
161
+ |-------|----------|
162
+ | `public` | **TPM2B_PUBLIC** wire bytes (size prefix + `TPMT_PUBLIC` structure) |
163
+ | `private` | **TPM2B_PRIVATE** wire bytes (encrypted sensitive area) |
164
+
165
+ These are **exportable wrapped key blobs**, not persistent TPM handles. Persist them in your app (encrypted at rest). To use again: `tpm.keys.load(blob)` or pass `akBlob` to flat `Tpm.quote` / `Tpm.activateCredential`.
166
+
167
+ **Important:** Windows PCP attestation blobs (`PCP1`/`PCP2` magic) are **not** valid for `tpm.keys.load` or `key.sign`. The library rejects them with `NOT_SUPPORTED`. Use `tpm.attest` / `AkHandle` for PCP blobs.
168
+
169
+ ### `TpmInfo`
170
+
171
+ ```typescript
172
+ type TpmInfo = {
173
+ manufacturer: string; // four-cc, e.g. "IFX ", "STM "
174
+ firmwareVersion: string;
175
+ isVirtual: boolean; // heuristic (swtpm, IBM, vendor string)
176
+ spec: string; // TPM 2.0 family indicator, typically "2.0"
177
+ };
178
+ ```
179
+
180
+ ### `QuoteResult`
181
+
182
+ ```typescript
183
+ type QuoteResult = { message: Buffer; signature: Buffer };
184
+ ```
185
+
186
+ | Field | Meaning |
187
+ |-------|---------|
188
+ | `message` | Raw **TPMS_ATTEST** structure from `TPM2_Quote` — send to your verifier for parsing |
189
+ | `signature` | Signature over `message` using the AK (ECDSA-SHA256 on Linux; RSASSA-SHA256 on Windows PCP RSA AK) |
190
+
191
+ Also pass `akPublicDer` (from provisioning) and `pcrSelection` / `qualifyingData` to the verifier out-of-band.
192
+
193
+ ### `ReadPublicResult`
194
+
195
+ ```typescript
196
+ type ReadPublicResult = { publicKeyDer: Buffer; name: Buffer };
197
+ ```
198
+
199
+ | Field | Meaning |
200
+ |-------|---------|
201
+ | `publicKeyDer` | SubjectPublicKeyInfo DER (RSA or EC) extracted from the object's public area |
202
+ | `name` | **TPM2B_NAME** — hash of the public area; used in policies and credential activation |
203
+
204
+ ### Handle strings for `readPublic`
205
+
206
+ Persistent handles use hex strings with optional `0x` prefix, e.g. `'0x81010001'` (RSA EK), `'0x81010002'` (ECC EK).
207
+
208
+ ---
209
+
210
+ ## Root API (`Tpm`)
211
+
212
+ ### `Tpm.isAvailable(): Promise<boolean>`
213
+
214
+ **Implemented.** Probes whether a TPM backend exists and is reachable.
215
+
216
+ 1. If `native.cjs` failed to load → `false`.
217
+ 2. On macOS → `false` (hard-coded).
218
+ 3. On Linux/Windows → native `is_available()` (device node / TBS presence).
219
+
220
+ Never throws. Use before `open()` in user-facing apps.
221
+
222
+ ---
223
+
224
+ ### `Tpm.open(): Promise<TpmHandle>`
225
+
226
+ **Implemented.** Returns a namespace handle. Throws `TPM_UNAVAILABLE` if `isAvailable()` would be false.
227
+
228
+ Does not perform I/O beyond the availability check. See [Handle API](#handle-api-tpmhandle).
229
+
230
+ ---
231
+
232
+ ### `Tpm.getFixedProperties(): Promise<TpmInfo>`
233
+
234
+ **Implemented.** Alias: `Tpm.info()`.
235
+
236
+ **Under the hood:** Marshals `TPM2_GetCapability` for `TPM_CAP_TPM_PROPERTIES`, reads manufacturer four-cc, firmware version fields, family indicator, and vendor strings. Virtual TPM detection is heuristic only.
237
+
238
+ **Use for:** Inventory, support diagnostics, detecting swtpm in CI.
239
+
240
+ **Not for:** Security decisions (virtual flag is not tamper-proof).
241
+
242
+ ---
243
+
244
+ ### `Tpm.randomBytes(count): Promise<Buffer>`
245
+
246
+ **Implemented.** Flat form of `tpm.random.bytes`.
247
+
248
+ **Under the hood:** `TPM2_GetRandom` in chunks of ≤64 bytes per TPM spec, concatenated to `count` bytes.
249
+
250
+ | Parameter | Type | Meaning |
251
+ |-----------|------|---------|
252
+ | `count` | `number` | Bytes requested; `0` returns empty Buffer |
253
+
254
+ **Use for:** Hardware-backed entropy, nonces, key material mixing.
255
+
256
+ **Not for:** High-throughput streaming (each chunk is a TPM round-trip); use OS CSPRNG for bulk data unless you specifically need TPM RNG.
257
+
258
+ ---
259
+
260
+ ### `Tpm.pcrRead(selection, bank?): Promise<Record<number, string>>`
261
+
262
+ **Implemented.** Flat form of `tpm.pcr.read`.
263
+
264
+ ---
265
+
266
+ ### `Tpm.pcrExtend(index, digest): Promise<void>`
267
+
268
+ **Implemented.** Flat form of `tpm.pcr.extend`.
269
+
270
+ ---
271
+
272
+ ### `Tpm.readPublic(handle): Promise<ReadPublicResult>`
273
+
274
+ **Implemented.** Flat form of `tpm.readPublic`.
275
+
276
+ ---
277
+
278
+ ### `Tpm.readEkCertificate(): Promise<Buffer | null>`
279
+
280
+ **Implemented.** Flat form of `tpm.attest.ekCertificate`.
281
+
282
+ ---
283
+
284
+ ### `Tpm.provisionAk(opts?): Promise<ProvisionAkResult>`
285
+
286
+ **Implemented.** Flat form of `tpm.attest.provisionAk`. Returns raw `{ akPublicDer, akBlob }` instead of `AkHandle`.
287
+
288
+ See [Attestation handles](#attestation-handles-akhandle) and [Attestation flows](#attestation-flows).
289
+
290
+ ---
291
+
292
+ ### `Tpm.quote(opts): Promise<QuoteResult>`
293
+
294
+ **Implemented.** Flat form of `tpm.attest.quote` / `ak.quote`.
295
+
296
+ Requires `opts.akBlob` in the options object (unlike handle method, which binds the blob).
297
+
298
+ ---
299
+
300
+ ### `Tpm.activateCredential(opts): Promise<Buffer>`
301
+
302
+ **Implemented.** Flat form of `ak.activateCredential`. Requires `opts.akBlob`.
303
+
304
+ Returns the recovered secret (typically 32-byte seed) from the verifier's MakeCredential step.
305
+
306
+ ---
307
+
308
+ ### `Tpm.createKey(opts?): Promise<{ publicKeyDer, keyBlob }>`
309
+
310
+ **Implemented.** Flat form of `tpm.keys.create`. Returns plain object, not `KeyHandle`.
311
+
312
+ ---
313
+
314
+ ### `Tpm.signKeyBlob({ keyBlob, digest }): Promise<Buffer>`
315
+
316
+ **Implemented.** Flat form of `key.sign`. Signs with a wrapped blob without constructing a `KeyHandle`.
317
+
318
+ | Parameter | Meaning |
319
+ |-----------|---------|
320
+ | `keyBlob` | `{ public, private }` from create/load |
321
+ | `digest` | **32-byte SHA-256** digest (not raw message) |
322
+
323
+ ---
324
+
325
+ ## Handle API (`TpmHandle`)
326
+
327
+ Returned by `Tpm.open()`. All sub-namespaces are plain objects with async methods.
328
+
329
+ ### `tpm.info(): Promise<TpmInfo>`
330
+
331
+ **Implemented.** Delegates to `Tpm.getFixedProperties()`.
332
+
333
+ ---
334
+
335
+ ### `tpm.readPublic(handle): Promise<ReadPublicResult>`
336
+
337
+ **Implemented.**
338
+
339
+ **Under the hood:**
340
+
341
+ 1. Parse handle string to `u32`.
342
+ 2. `TPM2_ReadPublic` via TBS.
343
+ 3. Convert `TPMT_PUBLIC` to SPKI DER; return name digest.
344
+
345
+ **Use for:** Reading EK public keys (`0x81010001`, `0x81010002`), verifying persistent objects.
346
+
347
+ **Not for:** Loading wrapped blobs (use `keys.load` instead).
348
+
349
+ ---
350
+
351
+ ### `tpm.pcr.read(selection, bank?): Promise<Record<number, string>>`
352
+
353
+ **Implemented.**
354
+
355
+ | Parameter | Default | Meaning |
356
+ |-----------|---------|---------|
357
+ | `selection` | — | Array of PCR indices (0–23 for SHA-256 bank) |
358
+ | `bank` | `'sha256'` | Only `'sha256'` supported today |
359
+
360
+ **Returns:** Map of index → **lowercase hex digest** (32 bytes = 64 hex chars for SHA-256).
361
+
362
+ **Under the hood:** Builds `TPML_PCR_SELECTION` for SHA-256, sends `TPM2_PCR_Read`, parses `TPML_DIGEST` in response order.
363
+
364
+ **Use for:** Remote attestation inputs, boot-state checks alongside quotes.
365
+
366
+ **Not for:** Extending PCRs (use [`tpm.pcr.extend`](#tpm-pcr-extendindex-digest)).
367
+
368
+ ---
369
+
370
+ ### `tpm.pcr.extend(index, digest): Promise<void>`
371
+
372
+ **Implemented.**
373
+
374
+ | Parameter | Meaning |
375
+ |-----------|---------|
376
+ | `index` | Single PCR index (0–23 for SHA-256 bank) |
377
+ | `digest` | **32-byte** SHA-256 measurement (`Buffer`) |
378
+
379
+ **Under the hood:** Sends `TPM2_PCR_Extend` with `authHandle = TPM_RH_NULL`, one SHA-256 digest in `TPML_DIGEST_VALUES`. The TPM updates the bank as `SHA256(old_pcr || digest)`.
380
+
381
+ **Caveats:**
382
+
383
+ - **Linux:** Firmware may lock specific indices (often **0–7**). Prefer **16–23** for application measurements.
384
+ - **Windows standard user:** TBS returns `TPM_E_COMMAND_BLOCKED` → library maps to **`REQUIRES_ELEVATION`** (re-run Admin PowerShell). Not `COMMAND_BLOCKED`.
385
+ - **Windows Administrator:** Can extend on real client hardware (validated). Does not affect quotes that only select other PCRs (e.g. `[0,1,7]`).
386
+
387
+ **Flat equivalent:** [`Tpm.pcrExtend(index, digest)`](#tpm-pcrextendindex-digest-promisevoid).
388
+
389
+ ---
390
+
391
+ ### `tpm.random.bytes(count): Promise<Buffer>`
392
+
393
+ **Implemented.** Same as [`Tpm.randomBytes`](#tpm-randombytescount-promisebuffer).
394
+
395
+ ---
396
+
397
+ ### `tpm.nv.read(handle, offset?, size?, auth?)`
398
+
399
+ **Implemented.**
400
+
401
+ ```typescript
402
+ // handle: hex string ('0x01c00002') or number
403
+ await tpm.nv.read('0x01c00002');
404
+ await tpm.nv.read('0x01c00002', 0, 512);
405
+ await tpm.nv.read('0x01800001', 0, undefined, authBuffer);
406
+ ```
407
+
408
+ **Under the hood:** `NV_ReadPublic` → size/attribute check → `NV_Read` with owner or index auth (based on `TPMA_NV_PPREAD` / `TPMA_NV_AUTHREAD`). Optional `auth` supplies the index password for `AUTHREAD` indices.
409
+
410
+ **Safe indices on consumer hardware:**
411
+
412
+ | Index | Typical use | Writable |
413
+ |-------|-------------|----------|
414
+ | `0x01c00002`, `0x01c0000A` | EK certificate (RSA / ECC) | **Read-only** (firmware) |
415
+ | `0x01c0000B` | EK template | Read-only |
416
+ | User-defined (`0x01800001`+) | Application data | **`nv.define` then read/write** |
417
+
418
+ Prefer read-only access to well-known TCG indices. Writes fail with `TPM_RC` / `AUTH_FAILED` when the index is not writable or auth is wrong.
419
+
420
+ **Flat equivalent:** [`Tpm.nvRead(handle, offset?, size?, auth?)`](#tpm-nvread).
421
+
422
+ ---
423
+
424
+ ### `tpm.nv.readPublic(handle): Promise<{ dataSize, attributes }>`
425
+
426
+ **Implemented.** Returns NV index metadata from `NV_ReadPublic` without reading data.
427
+
428
+ **Flat equivalent:** [`Tpm.nvReadPublic(handle)`](#tpm-nvreadpublic).
429
+
430
+ ---
431
+
432
+ ### `tpm.nv.define(opts): Promise<void>`
433
+
434
+ **Implemented.** Creates an owner NV index (`TPM2_NV_DefineSpace`).
435
+
436
+ ```typescript
437
+ type NvDefineOptions = {
438
+ handle: string | number; // 0x01800000..0x01BFFFFF
439
+ size: number; // 1..65535 bytes
440
+ auth?: Buffer; // index password (if using AUTH* attributes)
441
+ ownerAuth?: Buffer; // owner hierarchy password (often empty)
442
+ };
443
+ ```
444
+
445
+ **Default attributes:** `OWNERREAD | OWNERWRITE | NO_DA` — read/write via owner auth on `TPM_RH_OWNER`.
446
+
447
+ **Destructive / privileged:** Consumes TPM NV space until [`tpm.nv.undefine`](#tpmnvundefinehandle-ownerauth). Refuses EK cert indices. **Not for production laptops without intent.**
448
+
449
+ **Flat equivalent:** [`Tpm.nvDefine(opts)`](#tpm-nvdefine).
450
+
451
+ ---
452
+
453
+ ### `tpm.nv.undefine(handle, ownerAuth?): Promise<void>`
454
+
455
+ **Implemented.** Deletes an owner NV index (`TPM2_NV_UndefineSpace`).
456
+
457
+ **Flat equivalent:** [`Tpm.nvUndefine(handle, ownerAuth?)`](#tpm-nvundefine).
458
+
459
+ ---
460
+
461
+ ### `tpm.nv.write(handle, data, offset?, auth?)`
462
+
463
+ **Implemented.**
464
+
465
+ **Under the hood:** `NV_ReadPublic` bounds check → `NV_Write` with owner or index auth (`TPMA_NV_PPWRITE` / `TPMA_NV_AUTHWRITE`).
466
+
467
+ **Caveats:** Most factory NV indices are read-only. User-defined indices must be created with [`tpm.nv.define`](#tpmnvdefineopts-promisevoid) first.
468
+
469
+ **Flat equivalent:** [`Tpm.nvWrite(handle, data, offset?, auth?)`](#tpm-nvwrite).
470
+
471
+ ---
472
+
473
+ ### `tpm.keys.create(opts): Promise<KeyHandle>`
474
+
475
+ **Implemented.**
476
+
477
+ ```typescript
478
+ type KeyCreateOptions = {
479
+ type: 'ecc' | 'rsa';
480
+ sign?: boolean; // default true
481
+ decrypt?: boolean; // default false; RSA only
482
+ };
483
+ ```
484
+
485
+ **Under the hood (both OSes, TBS path):**
486
+
487
+ 1. `CreatePrimary` — transient ECC P-256 storage primary under owner hierarchy.
488
+ 2. `Create` — child signing key with `userWithAuth`, `fixedTPM`, `fixedParent`, `sensitiveDataOrigin`.
489
+ 3. Flush storage primary; return wrapped `{ public, private }` + SPKI DER.
490
+
491
+ | Key type | Signing scheme | Notes |
492
+ |----------|----------------|-------|
493
+ | `ecc` | ECDSA-SHA256, NIST P-256 | Default |
494
+ | `rsa` | RSASSA-SHA256, 2048-bit, exponent 65537 | |
495
+
496
+ At least one of `sign` or `decrypt` must be true. `decrypt: true` with `type: 'ecc'` is rejected (`INVALID_ARGUMENT`).
497
+
498
+ **Use for:** Device-bound signing keys, software-held wrapped blobs, same-user persistence.
499
+
500
+ **Not for:** Attestation quotes (use `attest.provisionAk` — different template and policy). Not for Windows cross-user fleet keys (use machine-scoped AK).
501
+
502
+ ---
503
+
504
+ ### `tpm.keys.load(blob): Promise<KeyHandle>`
505
+
506
+ **Implemented.**
507
+
508
+ **Under the hood:**
509
+
510
+ 1. Parse SPKI from `blob.public` via `keyBlobPublicDer` (validates TBS blob, rejects PCP).
511
+ 2. Return `KeyHandle` wrapping the blob (no TPM load until `sign`).
512
+
513
+ **Use for:** Restoring previously exported keys.
514
+
515
+ ---
516
+
517
+ ### `tpm.seal.seal(opts)` / `tpm.seal.unseal(blob)`
518
+
519
+ **Implemented.**
520
+
521
+ ```typescript
522
+ type SealOptions = {
523
+ data: Buffer;
524
+ pcrSelection?: number[]; // SHA-256 bank; binds to current PCR values at seal time
525
+ };
526
+
527
+ const sealed = await tpm.seal.seal({ data: secret });
528
+ const plain = await tpm.seal.unseal(sealed);
529
+
530
+ // PCR-bound: unseal fails if PCR state changes
531
+ const bound = await tpm.seal.seal({ data: secret, pcrSelection: [7] });
532
+ ```
533
+
534
+ **Under the hood:**
535
+
536
+ 1. `CreatePrimary` — transient storage primary.
537
+ 2. Optional `PolicyPCR` session when `pcrSelection` is set; policy digest embedded in sealed object.
538
+ 3. `Create` — keyedhash sealed object (`fixedTPM | fixedParent | userWithAuth | noDA`).
539
+ 4. Export `SEAL` wire blob (public + private + PCR metadata).
540
+ 5. `unseal`: load + `Unseal` (with matching `PolicyPCR` when bound).
541
+
542
+ **Caveats:** PCR-bound seal requires the chosen PCRs to match at unseal time. `tpm.pcr.extend` on Windows needs elevation for many indices.
543
+
544
+ **Flat equivalents:** [`Tpm.seal(opts)`](#tpm-sealopts), [`Tpm.unseal(blob)`](#tpm-unsealblob).
545
+
546
+ ---
547
+
548
+ ### `tpm.attest.ekCertificate(): Promise<Buffer | null>`
549
+
550
+ **Implemented.**
551
+
552
+ **Under the hood:** Tries TCG standard EK cert NV indices `0x01c00002` then `0x01c0000A`. For each: `NV_ReadPublic` → `NV_Read` with owner auth. Returns DER certificate bytes, or `null` if neither index is provisioned.
553
+
554
+ **Use for:** Chain-of-trust to manufacturer EK cert in attestation verification.
555
+
556
+ **Not for:** Attestation quotes (use `attest.provisionAk`). For general NV access use [`tpm.nv.read`](#tpmnvreadhandle-offset-size-auth).
557
+
558
+ ---
559
+
560
+ ### `tpm.attest.provisionAk(opts?): Promise<AkHandle>`
561
+
562
+ **Implemented.**
563
+
564
+ ```typescript
565
+ type ProvisionAkOptions = {
566
+ keyName?: string; // Windows PCP persisted name; random if omitted
567
+ scope?: 'user' | 'machine'; // Windows only; default 'user'
568
+ overwrite?: boolean; // Windows: replace existing name
569
+ algorithm?: 'ecc' | 'rsa'; // deprecated; Windows always RSA via PCP
570
+ };
571
+ ```
572
+
573
+ **Linux under the hood:**
574
+
575
+ 1. Create storage primary.
576
+ 2. Create AK with `adminWithPolicy` digest for ActivateCredential, ECC P-256.
577
+ 3. Export TPM2B public/private; flush transients.
578
+ 4. Return `AkHandle`.
579
+
580
+ **Windows under the hood:**
581
+
582
+ 1. NCrypt PCP `CreatePersistedKey` / identity key (RSA-2048).
583
+ 2. Blob format is PCP-specific (`PCP1`/`PCP2`); persisted under `keyName`.
584
+ 3. `scope: 'machine'` sets DACL for standard-user quote at runtime (requires elevation at provision time).
585
+
586
+ See [Attestation flows](#attestation-flows) and [windows-pcp.md](./windows-pcp.md).
587
+
588
+ ---
589
+
590
+ ### `tpm.attest.quote(opts): Promise<QuoteResult>`
591
+
592
+ **Implemented.** Same as `ak.quote` but requires `opts.akBlob`.
593
+
594
+ **Under the hood:**
595
+
596
+ 1. Create storage primary (Linux) or open PCP key (Windows).
597
+ 2. Load AK from blob.
598
+ 3. `TPM2_Quote` with PCR selection list, qualifying data (nonce), signature scheme (ECDSA-SHA256 Linux; TPM_ALG_NULL → RSASSA on Windows PCP).
599
+ 4. Flush all transient handles.
600
+
601
+ | Parameter | Meaning |
602
+ |-----------|---------|
603
+ | `pcrSelection` | PCR indices to include in quote |
604
+ | `qualifyingData` | Server challenge / nonce (binds quote to session) |
605
+ | `bank` | `'sha256'` (default) |
606
+
607
+ ---
608
+
609
+ ### `tpm[Symbol.asyncDispose]()`
610
+
611
+ **Implemented (no-op).** Reserved for future resource cleanup.
612
+
613
+ ---
614
+
615
+ ## Key handles (`KeyHandle`)
616
+
617
+ Returned by `tpm.keys.create()` and `tpm.keys.load()`.
618
+
619
+ ### `key.export(): KeyBlob`
620
+
621
+ **Implemented.** Returns `{ public, private }` Buffers for persistence. Same shape as `AkBlob`.
622
+
623
+ ---
624
+
625
+ ### `key.publicKeyDer` (getter)
626
+
627
+ **Implemented.** Read-only SPKI DER for the public key.
628
+
629
+ ---
630
+
631
+ ### `key.sign(digest): Promise<Buffer>`
632
+
633
+ **Implemented.**
634
+
635
+ **Under the hood:**
636
+
637
+ 1. Create storage primary, `Load` wrapped blob.
638
+ 2. `TPM2_Sign` with 32-byte digest, external hash ticket (caller must pre-hash with SHA-256).
639
+ 3. Flush primary and key handles.
640
+
641
+ | Parameter | Meaning |
642
+ |-----------|---------|
643
+ | `digest` | **Exactly 32 bytes** — SHA-256 hash of message |
644
+
645
+ **Returns:** TPM2B signature wire bytes (DER-encoded signature inside).
646
+
647
+ **Use for:** Proof-of-possession, document signing with device key.
648
+
649
+ **Not for:** Raw message signing (hash in application first). Not for PCP AK blobs.
650
+
651
+ ---
652
+
653
+ ### `key.decrypt(cipher)`
654
+
655
+ **Implemented.** RSA OAEP (SHA-256) via `TPM2_RSA_Decrypt`. Key must have been created with `decrypt: true` (RSA only).
656
+
657
+ **Under the hood:** Regenerate storage primary → `Load` → `RSA_Decrypt` with explicit OAEP scheme → flush.
658
+
659
+ **Use for:** Decrypting ciphertext produced for the TPM RSA key's public half.
660
+
661
+ **Not for:** ECC keys or sign-only RSA keys (`INVALID_ARGUMENT`).
662
+
663
+ ---
664
+
665
+ ## Attestation handles (`AkHandle`)
666
+
667
+ Returned by `tpm.attest.provisionAk()`.
668
+
669
+ ### `ak.export(): AkBlob`
670
+
671
+ **Implemented.** Wrapped public/private for persistence. On Windows with PCP, this is the PCP blob — store securely for runtime quote.
672
+
673
+ ---
674
+
675
+ ### `ak.publicKeyDer` (getter)
676
+
677
+ **Implemented.** SPKI DER of the attestation key public area.
678
+
679
+ ---
680
+
681
+ ### `ak.quote(opts): Promise<QuoteResult>`
682
+
683
+ **Implemented.** Same as [`tpm.attest.quote`](#tpm-attest-quoteopts-promisetquoteresult) without passing `akBlob`.
684
+
685
+ ---
686
+
687
+ ### `ak.activateCredential(opts): Promise<Buffer>`
688
+
689
+ **Implemented.**
690
+
691
+ ```typescript
692
+ type ActivateCredentialOptions = {
693
+ credentialBlob: Buffer; // from verifier MakeCredential
694
+ secret: Buffer; // encrypted seed from MakeCredential
695
+ };
696
+ ```
697
+
698
+ **Under the hood:**
699
+
700
+ 1. Load AK from blob.
701
+ 2. Build policy session: `PolicySecret` (endorsement) + `PolicyCommandCode` (ActivateCredential).
702
+ 3. **Linux:** `TPM2_ActivateCredential` via TBS with EK handle.
703
+ 4. **Windows:** `NCryptDecrypt` with `NCRYPT_PCP_TPM12_IDACTIVATION_MAGIC` (PCP path — raw TBS activation is blocked for standard users).
704
+
705
+ **Returns:** Recovered credential secret (proves AK resides on same TPM as EK).
706
+
707
+ **Use for:** Enrollment proof-of-possession before trusting `akPublicDer`.
708
+
709
+ **Not for:** General decryption. On Windows, standard users cannot activate — enrollment must run elevated; runtime quote does not need activation.
710
+
711
+ ---
712
+
713
+ ## Attestation flows
714
+
715
+ ### Threat model (read this for fleet keys)
716
+
717
+ Attestation keys prove **device identity**, not **application identity**.
718
+
719
+ - **Windows machine scope (`PCP2`):** The AK blob is a locator (`keyName` + scope), not a shared secret. Standard users with the fleet `keyName` can quote — by design, so runtime apps need no admin. A quote answers: “this enrolled TPM, these PCRs, this challenge.” It does not answer: “only my.exe ran.”
720
+ - **Linux:** Wrapped AK blobs are sensitive on shared hosts: any principal with `/dev/tpmrm0` access can load the blob and quote on that TPM.
721
+ - **Enrollment vs runtime:** Bind the device at enroll time (verify `akPublicDer`, register with your service). Use `qualifyingData` on each quote for replay resistance. User/app binding is your session layer, not the TPM quote alone.
722
+
723
+ See [windows-pcp.md](./windows-pcp.md#threat-model-device-vs-application) for the full Windows PCP framing.
724
+
725
+ ### Dev / same-user (Linux and Windows)
726
+
727
+ ```javascript
728
+ import { Tpm } from 'node-tpm2';
729
+
730
+ await using tpm = await Tpm.open();
731
+
732
+ const ak = await tpm.attest.provisionAk();
733
+ const challenge = Buffer.from('server-session-nonce');
734
+
735
+ const { message, signature } = await ak.quote({
736
+ pcrSelection: [0, 1, 7],
737
+ qualifyingData: challenge,
738
+ });
739
+
740
+ // Send to verifier: ak.publicKeyDer, message, signature, pcrSelection, challenge
741
+ const saved = ak.export();
742
+ ```
743
+
744
+ Verifier checks: signature over `message`, PCR values inside attestation, `qualifyingData` matches challenge, optional EK cert chain.
745
+
746
+ ### Persist and reload
747
+
748
+ ```javascript
749
+ const saved = ak.export();
750
+ // ... store JSON { public: b64, private: b64 } encrypted ...
751
+
752
+ const reloaded = await tpm.attest.quote({
753
+ akBlob: saved,
754
+ pcrSelection: [0, 1, 7],
755
+ qualifyingData: challenge,
756
+ });
757
+ // Or on Windows/Linux after reload, use flat Tpm.quote({ akBlob: saved, ... })
758
+ ```
759
+
760
+ ### Windows fleet enrollment (machine scope)
761
+
762
+ **Once (Admin or SYSTEM):**
763
+
764
+ ```javascript
765
+ const ak = await tpm.attest.provisionAk({
766
+ keyName: 'my-app-device-ak',
767
+ scope: 'machine',
768
+ overwrite: true,
769
+ });
770
+ // Persist ak.export(); register ak.publicKeyDer with enrollment service
771
+ ```
772
+
773
+ **Every session (standard user):**
774
+
775
+ ```javascript
776
+ const akBlob = loadPersistedBlob();
777
+ const quote = await Tpm.quote({
778
+ akBlob,
779
+ pcrSelection: [0, 1, 7],
780
+ qualifyingData: runtimeChallenge,
781
+ });
782
+ ```
783
+
784
+ See [windows-pcp.md](./windows-pcp.md) for DACL and SYSTEM provisioning details.
785
+
786
+ ### EK certificate + credential activation
787
+
788
+ ```javascript
789
+ await using tpm = await Tpm.open();
790
+
791
+ const ekCert = await tpm.attest.ekCertificate(); // null if OEM didn't provision NV
792
+ const ek = await tpm.readPublic('0x81010001'); // RSA EK handle
793
+
794
+ // Verifier runs MakeCredential(ekPublic, akName, secret) → credentialBlob + secret
795
+ const recovered = await ak.activateCredential({ credentialBlob, secret });
796
+ // recovered proves AK is on TPM that owns EK
797
+ ```
798
+
799
+ ---
800
+
801
+ ## Error model (`TpmError`)
802
+
803
+ ```javascript
804
+ import { Tpm, TpmError } from 'node-tpm2';
805
+
806
+ try {
807
+ await Tpm.open();
808
+ } catch (err) {
809
+ if (err instanceof TpmError) {
810
+ err.code; // stable string — branch on this
811
+ err.message; // human-readable detail
812
+ err.suggestion; // optional remediation hint
813
+ err.tpmRc; // TPM 2.0 response code (number), when present
814
+ err.hresult; // Windows NCrypt HRESULT (number), when present
815
+ }
816
+ }
817
+ ```
818
+
819
+ Native Rust errors serialize as `__tpm2__code|message|suggestion|tpmRc|hresult` and are parsed in `api.js`.
820
+
821
+ ### Stable error codes
822
+
823
+ | Code | When |
824
+ |------|------|
825
+ | `TPM_UNAVAILABLE` | No TPM, missing native binary, macOS, backend not built |
826
+ | `ACCESS_DENIED` | OS denied device access (Linux permissions, container) |
827
+ | `REQUIRES_ELEVATION` | Windows Admin/SYSTEM required (machine AK, activation) |
828
+ | `COMMAND_BLOCKED` | Windows TBS driver blocked command ordinal |
829
+ | `NOT_SUPPORTED` | PCP/TBS capability gap on this platform | — | sometimes | — |
830
+ | `INVALID_ARGUMENT` | Bad options (wrong digest size, invalid key type, empty `keyName`) |
831
+ | `KEY_NOT_FOUND` | NCrypt persisted key missing |
832
+ | `ALREADY_EXISTS` | NCrypt key name collision (`overwrite: false`) |
833
+ | `MARSHALLING_ERROR` | Codec bug, malformed command, some NCrypt failures |
834
+ | `TRANSPORT_ERROR` | TBS / `/dev/tpmrm0` I/O failure |
835
+ | `AUTH_FAILED` | TPM auth-class response (policy/password) |
836
+ | `TPM_RC` | Other non-success TPM response |
837
+
838
+ TPM response codes map by class: auth → `AUTH_FAILED`, format → `MARSHALLING_ERROR`, Windows `0x80280400` → `COMMAND_BLOCKED`, else → `TPM_RC`.
839
+
840
+ ---
841
+
842
+ ## Platform differences
843
+
844
+ | Concern | Linux | Windows |
845
+ |---------|-------|---------|
846
+ | Transport | `/dev/tpmrm0` (resource manager) | TBS `Tbsip_Submit_Command` |
847
+ | Device access | User in `tss` group or equivalent | Standard user for most TBS ops |
848
+ | General `keys.*` | TBS wrapped blobs, ECC default | Same TBS path and blob format |
849
+ | `attest.provisionAk` | TBS ECC P-256 AK, transient export | NCrypt PCP RSA-2048 persisted key |
850
+ | Quote crypto | ECDSA-SHA256 explicit | RSASSA-SHA256 (PCP default scheme) |
851
+ | ActivateCredential | Full TBS policy path | NCrypt PCP ID activation |
852
+ | EK certificate | NV indices via TBS | Same |
853
+ | macOS | `isAvailable()` → false | — |
854
+
855
+ **Blob interchange:** Linux AK blobs are **not** portable to Windows PCP and vice versa. Fleet apps standardize on one platform's provisioning flow per device.
856
+
857
+ ---
858
+
859
+ ## Privilege matrix
860
+
861
+ | API | Linux user | Windows user | Windows Admin/SYSTEM |
862
+ |-----|:----------:|:------------:|:--------------------:|
863
+ | `Tpm.isAvailable()`, `open()`, `info()` | ✓ | ✓ | ✓ |
864
+ | `tpm.random.bytes`, `tpm.pcr.read` | ✓ | ✓ | ✓ |
865
+ | `tpm.pcr.extend` | ✓ † | ✗ → `REQUIRES_ELEVATION` | ✓ † |
866
+ | `tpm.nv.read` / `tpm.nv.write` | ✓ ‡ | ✓ ‡ | ✓ |
867
+ | `tpm.nv.define` / `tpm.nv.undefine` | ✓ § | ✓ § | ✓ § |
868
+ | `tpm.keys.create/load`, `key.sign`, `key.decrypt` | ✓ | ✓ | ✓ |
869
+ | `tpm.seal.seal` / `tpm.seal.unseal` | ✓ | ✓ | ✓ |
870
+ | `tpm.attest.provisionAk()` user scope | ✓ | ✓ | ✓ |
871
+ | `tpm.attest.provisionAk({ scope: 'machine' })` | — | ✗ | ✓ |
872
+ | `ak.quote` / `Tpm.quote` | ✓ | ✓ | ✓ |
873
+ | `ak.activateCredential` | ✓ | ✗ | ✓ |
874
+
875
+ † **`pcr.extend`:** Linux user OK (avoid boot PCRs 0–7). Windows user blocked → **`REQUIRES_ELEVATION`**; Admin/SYSTEM OK. See [windows-pcp.md](./windows-pcp.md).
876
+
877
+ ‡ **`nv.read/write`:** Index permissions vary; EK cert indices are read-only. Writes to undefined indices fail at the TPM.
878
+
879
+ § **`nv.define/undefine`:** Owner authorization required; owner NV range only. Destructive on NV space.
880
+
881
+ Linux requires read/write on `/dev/tpmrm0`. Windows fleet pattern: provision machine AK once elevated → standard users quote forever.
882
+
883
+ ---
884
+
885
+ ## Deferred / not in public API
886
+
887
+ | Feature | Notes |
888
+ |---------|-------|
889
+ | *(none — all planned namespaces are implemented)* | See [roadmap](./roadmap.md) for hardening / polish |
890
+
891
+ ---
892
+
893
+ ## Namespace comparison table
894
+
895
+ | Operation | Namespace API | Flat API | Returns |
896
+ |-----------|---------------|----------|---------|
897
+ | Open session | `await Tpm.open()` | — | `TpmHandle` |
898
+ | TPM info | `tpm.info()` | `Tpm.info()` / `getFixedProperties()` | `TpmInfo` |
899
+ | Random | `tpm.random.bytes(n)` | `Tpm.randomBytes(n)` | `Buffer` |
900
+ | PCR read | `tpm.pcr.read(sel, bank?)` | `Tpm.pcrRead(sel, bank?)` | `Record<number, string>` |
901
+ | PCR extend | `tpm.pcr.extend(i, d)` | `Tpm.pcrExtend(i, d)` | `void` |
902
+ | NV read | `tpm.nv.read(h, off?, sz?, auth?)` | `Tpm.nvRead(...)` | `Buffer` |
903
+ | NV write | `tpm.nv.write(h, data, off?, auth?)` | `Tpm.nvWrite(...)` | `void` |
904
+ | NV readPublic | `tpm.nv.readPublic(h)` | `Tpm.nvReadPublic(h)` | `{ dataSize, attributes }` |
905
+ | NV define | `tpm.nv.define(opts)` | `Tpm.nvDefine(opts)` | `void` |
906
+ | NV undefine | `tpm.nv.undefine(h, ownerAuth?)` | `Tpm.nvUndefine(...)` | `void` |
907
+ | Create key | `tpm.keys.create(opts)` | `Tpm.createKey(opts)` | `KeyHandle` / `{ publicKeyDer, keyBlob }` |
908
+ | Load key | `tpm.keys.load(blob)` | — | `KeyHandle` |
909
+ | Sign | `key.sign(digest)` | `Tpm.signKeyBlob({ keyBlob, digest })` | `Buffer` |
910
+ | Decrypt | `key.decrypt(cipher)` | `Tpm.decryptKeyBlob({ keyBlob, cipher })` | `Buffer` |
911
+ | Seal | `tpm.seal.seal/unseal` | `Tpm.seal` / `Tpm.unseal` | `Buffer` |
912
+ | EK cert | `tpm.attest.ekCertificate()` | `Tpm.readEkCertificate()` | `Buffer \| null` |
913
+ | Provision AK | `tpm.attest.provisionAk(opts)` | `Tpm.provisionAk(opts)` | `AkHandle` / `{ akPublicDer, akBlob }` |
914
+ | Quote | `ak.quote(opts)` / `tpm.attest.quote(opts)` | `Tpm.quote({ akBlob, ... })` | `QuoteResult` |
915
+ | Activate | `ak.activateCredential(opts)` | `Tpm.activateCredential({ akBlob, ... })` | `Buffer` |
916
+ | ReadPublic | `tpm.readPublic(h)` | `Tpm.readPublic(h)` | `ReadPublicResult` |
917
+
918
+ ---
919
+
920
+ ## Code examples
921
+
922
+ ### Complete signing workflow
923
+
924
+ ```javascript
925
+ import { createHash } from 'node:crypto';
926
+ import { Tpm } from 'node-tpm2';
927
+
928
+ await using tpm = await Tpm.open();
929
+
930
+ const key = await tpm.keys.create({ type: 'ecc', sign: true });
931
+ const digest = createHash('sha256').update('document-body').digest();
932
+ const signature = await key.sign(digest);
933
+
934
+ const blob = key.export();
935
+ const restored = await tpm.keys.load(blob);
936
+ await restored.sign(digest);
937
+ ```
938
+
939
+ ### Attestation with error handling
940
+
941
+ ```javascript
942
+ import { Tpm, TpmError } from 'node-tpm2';
943
+
944
+ try {
945
+ await using tpm = await Tpm.open();
946
+ const ak = await tpm.attest.provisionAk({ scope: 'machine', keyName: 'fleet-ak' });
947
+ } catch (err) {
948
+ if (err instanceof TpmError && err.code === 'REQUIRES_ELEVATION') {
949
+ // Re-run installer as Admin/SYSTEM
950
+ } else if (err instanceof TpmError && err.code === 'ALREADY_EXISTS') {
951
+ // Use overwrite: true
952
+ }
953
+ throw err;
954
+ }
955
+ ```
956
+
957
+ ### One-shot flat API (no handle)
958
+
959
+ ```javascript
960
+ import { Tpm } from 'node-tpm2';
961
+
962
+ if (!(await Tpm.isAvailable())) process.exit(1);
963
+
964
+ const props = await Tpm.getFixedProperties();
965
+ const random = await Tpm.randomBytes(32);
966
+ const pcrs = await Tpm.pcrRead([0, 7], 'sha256');
967
+ ```
968
+
969
+ ---
970
+
971
+ ## Symbol index
972
+
973
+ All public exports from `'node-tpm2'`:
974
+
975
+ | Symbol | Kind | Status |
976
+ |--------|------|--------|
977
+ | `TpmError` | class | Implemented |
978
+ | `Tpm.isAvailable` | function | Implemented |
979
+ | `Tpm.open` | function | Implemented |
980
+ | `Tpm.getFixedProperties` | function | Implemented |
981
+ | `Tpm.info` | function | Implemented |
982
+ | `Tpm.randomBytes` | function | Implemented |
983
+ | `Tpm.pcrRead` | function | Implemented |
984
+ | `Tpm.pcrExtend` | function | Implemented |
985
+ | `Tpm.readPublic` | function | Implemented |
986
+ | `Tpm.readEkCertificate` | function | Implemented |
987
+ | `Tpm.quote` | function | Implemented |
988
+ | `Tpm.provisionAk` | function | Implemented |
989
+ | `Tpm.activateCredential` | function | Implemented |
990
+ | `Tpm.createKey` | function | Implemented |
991
+ | `Tpm.signKeyBlob` | function | Implemented |
992
+ | `Tpm.decryptKeyBlob` | function | Implemented |
993
+ | `Tpm.nvRead` | function | Implemented |
994
+ | `Tpm.nvWrite` | function | Implemented |
995
+ | `Tpm.nvReadPublic` | function | Implemented |
996
+ | `Tpm.nvDefine` | function | Implemented |
997
+ | `Tpm.nvUndefine` | function | Implemented |
998
+ | `Tpm.seal` | function | Implemented |
999
+ | `Tpm.unseal` | function | Implemented |
1000
+ | `TpmHandle.info` | method | Implemented |
1001
+ | `TpmHandle.readPublic` | method | Implemented |
1002
+ | `TpmHandle.pcr.read` | method | Implemented |
1003
+ | `TpmHandle.pcr.extend` | method | Implemented |
1004
+ | `TpmHandle.random.bytes` | method | Implemented |
1005
+ | `TpmHandle.nv.read` | method | Implemented |
1006
+ | `TpmHandle.nv.write` | method | Implemented |
1007
+ | `TpmHandle.nv.readPublic` | method | Implemented |
1008
+ | `TpmHandle.nv.define` | method | Implemented |
1009
+ | `TpmHandle.nv.undefine` | method | Implemented |
1010
+ | `TpmHandle.keys.create` | method | Implemented |
1011
+ | `TpmHandle.keys.load` | method | Implemented |
1012
+ | `TpmHandle.seal.seal` | method | Implemented |
1013
+ | `TpmHandle.seal.unseal` | method | Implemented |
1014
+ | `TpmHandle.attest.ekCertificate` | method | Implemented |
1015
+ | `TpmHandle.attest.provisionAk` | method | Implemented |
1016
+ | `TpmHandle.attest.quote` | method | Implemented |
1017
+ | `TpmHandle[Symbol.asyncDispose]` | method | Implemented (no-op) |
1018
+ | `KeyHandle.export` | method | Implemented |
1019
+ | `KeyHandle.publicKeyDer` | getter | Implemented |
1020
+ | `KeyHandle.sign` | method | Implemented |
1021
+ | `KeyHandle.decrypt` | method | Implemented |
1022
+ | `AkHandle.export` | method | Implemented |
1023
+ | `AkHandle.publicKeyDer` | getter | Implemented |
1024
+ | `AkHandle.quote` | method | Implemented |
1025
+ | `AkHandle.activateCredential` | method | Implemented |
1026
+
1027
+ **TypeScript types** (from `index.d.ts`, not runtime exports): `TpmErrorCode`, `AkBlob`, `KeyBlob`, `QuoteOptions`, `QuoteResult`, `ReadPublicResult`, `ProvisionAkOptions`, `ProvisionAkResult`, `ActivateCredentialOptions`, `ActivateCredentialFlatOptions`, `KeyCreateOptions`, `SealOptions`, `TpmInfo`, `TpmHandle`, `KeyHandle`, `AkHandle`.
1028
+
1029
+ **Native-only** (used internally, not exported from package): `keyBlobPublicDer`.