| #!/usr/bin/env node | ||
| /** | ||
| * NV define / read / write / undefine cycle (owner NV index). | ||
| * | ||
| * WARNING: Mutates TPM NV storage. Use only on a test machine. | ||
| * Requires owner authorization (often empty password on consumer TPMs). | ||
| * | ||
| * Usage: | ||
| * node nv-smoke.mjs | ||
| * node nv-smoke.mjs --handle 0x01800042 --size 64 | ||
| * | ||
| * Install: | ||
| * npm install node-tpm2@beta | ||
| * node node_modules/node-tpm2/examples/nv-smoke.mjs | ||
| */ | ||
| import { Tpm } from '../index.js'; | ||
| function flagValue(args, name) { | ||
| for (let i = 0; i < args.length; i++) { | ||
| if (args[i] === name && args[i + 1]) return args[++i]; | ||
| const prefix = `${name}=`; | ||
| if (args[i]?.startsWith(prefix)) return args[i].slice(prefix.length); | ||
| } | ||
| return undefined; | ||
| } | ||
| async function main() { | ||
| const args = process.argv.slice(2); | ||
| const handle = flagValue(args, '--handle') ?? '0x01800042'; | ||
| const size = Number(flagValue(args, '--size') ?? '64'); | ||
| if (!(await Tpm.isAvailable())) { | ||
| console.error('FAIL: no TPM'); | ||
| process.exit(1); | ||
| } | ||
| await using tpm = await Tpm.open(); | ||
| const payload = Buffer.from(`node-tpm2-nv-smoke-${Date.now()}`); | ||
| console.log(`== nv-smoke handle=${handle} size=${size} ==`); | ||
| try { | ||
| await tpm.nv.undefine(handle); | ||
| console.log(' (pre-clean undefine OK or index absent)'); | ||
| } catch { | ||
| console.log(' (pre-clean undefine skipped — index may not exist)'); | ||
| } | ||
| await tpm.nv.define({ handle, size }); | ||
| console.log('PASS nv.define'); | ||
| const meta = await tpm.nv.readPublic(handle); | ||
| console.log('PASS nv.readPublic', meta); | ||
| await tpm.nv.write(handle, payload, 0); | ||
| console.log('PASS nv.write', payload.length, 'bytes'); | ||
| const readBack = await tpm.nv.read(handle, 0, payload.length); | ||
| if (!readBack.equals(payload)) { | ||
| console.error('FAIL: read mismatch'); | ||
| process.exit(1); | ||
| } | ||
| console.log('PASS nv.read roundtrip'); | ||
| await tpm.nv.undefine(handle); | ||
| console.log('PASS nv.undefine'); | ||
| console.log('\nnv-smoke: OK'); | ||
| } | ||
| main().catch((err) => { | ||
| console.error('FAIL:', err.message ?? err); | ||
| if (err.code) console.error(' code:', err.code); | ||
| if (err.tpmRc != null) console.error(' tpmRc:', err.tpmRc); | ||
| process.exit(1); | ||
| }); |
+135
-9
@@ -69,6 +69,7 @@ let native = null; | ||
| function notSupported(feature) { | ||
| return async () => { | ||
| throw new TpmError('NOT_SUPPORTED', `${feature} is not implemented yet.`); | ||
| }; | ||
| function parseTpmHandle(handle) { | ||
| if (typeof handle === 'number') { | ||
| return `0x${handle.toString(16).padStart(8, '0')}`; | ||
| } | ||
| return handle; | ||
| } | ||
@@ -98,3 +99,10 @@ | ||
| decrypt: notSupported('key.decrypt'), | ||
| decrypt: wrapNative(async (cipher) => { | ||
| requireNative('decryptKeyBlob'); | ||
| const plain = await native.decryptKeyBlob({ | ||
| keyBlob, | ||
| cipher, | ||
| }); | ||
| return Buffer.from(plain); | ||
| }), | ||
| }; | ||
@@ -169,4 +177,45 @@ } | ||
| nv: { | ||
| read: notSupported('tpm.nv.read'), | ||
| write: notSupported('tpm.nv.write'), | ||
| readPublic: wrapNative(async (handle) => { | ||
| requireNative('nvReadPublic'); | ||
| return native.nvReadPublic(parseTpmHandle(handle)); | ||
| }), | ||
| read: wrapNative(async (handle, offset, size, auth) => { | ||
| requireNative('nvRead'); | ||
| const buf = await native.nvRead( | ||
| parseTpmHandle(handle), | ||
| offset ?? undefined, | ||
| size ?? undefined, | ||
| auth ?? undefined, | ||
| ); | ||
| return Buffer.from(buf); | ||
| }), | ||
| write: wrapNative(async (handle, data, offset, auth) => { | ||
| requireNative('nvWrite'); | ||
| await native.nvWrite({ | ||
| handle: parseTpmHandle(handle), | ||
| data, | ||
| offset: offset ?? undefined, | ||
| auth: auth ?? undefined, | ||
| }); | ||
| }), | ||
| define: wrapNative(async (opts) => { | ||
| requireNative('nvDefine'); | ||
| await native.nvDefine({ | ||
| handle: parseTpmHandle(opts.handle), | ||
| size: opts.size, | ||
| auth: opts.auth ?? undefined, | ||
| ownerAuth: opts.ownerAuth ?? undefined, | ||
| }); | ||
| }), | ||
| undefine: wrapNative(async (handle, ownerAuth) => { | ||
| requireNative('nvUndefine'); | ||
| await native.nvUndefine({ | ||
| handle: parseTpmHandle(handle), | ||
| ownerAuth: ownerAuth ?? undefined, | ||
| }); | ||
| }), | ||
| }, | ||
@@ -193,4 +242,16 @@ | ||
| seal: { | ||
| seal: notSupported('tpm.seal'), | ||
| unseal: notSupported('tpm.unseal'), | ||
| seal: wrapNative(async (opts) => { | ||
| requireNative('seal'); | ||
| const buf = await native.seal({ | ||
| data: opts.data, | ||
| pcrSelection: opts.pcrSelection, | ||
| }); | ||
| return Buffer.from(buf); | ||
| }), | ||
| unseal: wrapNative(async (blob) => { | ||
| requireNative('unseal'); | ||
| const plain = await native.unseal(blob); | ||
| return Buffer.from(plain); | ||
| }), | ||
| }, | ||
@@ -359,2 +420,67 @@ | ||
| }), | ||
| decryptKeyBlob: wrapNative(async (opts) => { | ||
| requireNative('decryptKeyBlob'); | ||
| const plain = await native.decryptKeyBlob(opts); | ||
| return Buffer.from(plain); | ||
| }), | ||
| nvRead: wrapNative(async (handle, offset, size, auth) => { | ||
| requireNative('nvRead'); | ||
| const buf = await native.nvRead( | ||
| parseTpmHandle(handle), | ||
| offset ?? undefined, | ||
| size ?? undefined, | ||
| auth ?? undefined, | ||
| ); | ||
| return Buffer.from(buf); | ||
| }), | ||
| nvWrite: wrapNative(async (handle, data, offset, auth) => { | ||
| requireNative('nvWrite'); | ||
| await native.nvWrite({ | ||
| handle: parseTpmHandle(handle), | ||
| data, | ||
| offset: offset ?? undefined, | ||
| auth: auth ?? undefined, | ||
| }); | ||
| }), | ||
| nvReadPublic: wrapNative(async (handle) => { | ||
| requireNative('nvReadPublic'); | ||
| return native.nvReadPublic(parseTpmHandle(handle)); | ||
| }), | ||
| nvDefine: wrapNative(async (opts) => { | ||
| requireNative('nvDefine'); | ||
| await native.nvDefine({ | ||
| handle: parseTpmHandle(opts.handle), | ||
| size: opts.size, | ||
| auth: opts.auth ?? undefined, | ||
| ownerAuth: opts.ownerAuth ?? undefined, | ||
| }); | ||
| }), | ||
| nvUndefine: wrapNative(async (handle, ownerAuth) => { | ||
| requireNative('nvUndefine'); | ||
| await native.nvUndefine({ | ||
| handle: parseTpmHandle(handle), | ||
| ownerAuth: ownerAuth ?? undefined, | ||
| }); | ||
| }), | ||
| seal: wrapNative(async (opts) => { | ||
| requireNative('seal'); | ||
| const buf = await native.seal({ | ||
| data: opts.data, | ||
| pcrSelection: opts.pcrSelection, | ||
| }); | ||
| return Buffer.from(buf); | ||
| }), | ||
| unseal: wrapNative(async (blob) => { | ||
| requireNative('unseal'); | ||
| const plain = await native.unseal(blob); | ||
| return Buffer.from(plain); | ||
| }), | ||
| }; |
+149
-35
@@ -30,3 +30,3 @@ # node-tpm2 API Reference | ||
| 13. [Privilege matrix](#privilege-matrix) | ||
| 14. [Planned / not yet implemented](#planned--not-yet-implemented) | ||
| 14. [Deferred / not in public API](#deferred--not-in-public-api) | ||
| 15. [Symbol index](#symbol-index) | ||
@@ -70,3 +70,3 @@ | ||
| - **General keys, PCR, random, ReadPublic, seal (planned)** use the shared TBS command path on both Linux and Windows so blobs and behavior stay aligned. | ||
| - **General keys, PCR, random, ReadPublic, NV, seal** use the shared TBS command path on both Linux and Windows so blobs and behavior stay aligned. | ||
| - **Attestation key persistence on Windows** uses NCrypt Platform Crypto Provider (PCP) because raw TBS cannot persist cross-user identity keys reliably. | ||
@@ -383,4 +383,8 @@ - **Transient TPM handles** are created and flushed inside each native call. You do not manage TPM handle slots in JavaScript. | ||
| **Caveats:** Firmware or platform policy may lock specific PCRs; failures surface as `TPM_RC` or `COMMAND_BLOCKED`. | ||
| **Caveats:** | ||
| - **Linux:** Firmware may lock specific indices (often **0–7**). Prefer **16–23** for application measurements. | ||
| - **Windows standard user:** TBS returns `TPM_E_COMMAND_BLOCKED` → library maps to **`REQUIRES_ELEVATION`** (re-run Admin PowerShell). Not `COMMAND_BLOCKED`. | ||
| - **Windows Administrator:** Can extend on real client hardware (validated). Does not affect quotes that only select other PCRs (e.g. `[0,1,7]`). | ||
| **Flat equivalent:** [`Tpm.pcrExtend(index, digest)`](#tpm-pcrextendindex-digest-promisevoid). | ||
@@ -396,14 +400,78 @@ | ||
| ### `tpm.nv.read(handle, offset?, size?)` | ||
| ### `tpm.nv.read(handle, offset?, size?, auth?)` | ||
| **NOT_SUPPORTED** — throws `NOT_SUPPORTED`. Planned Phase 4. EK certificate today uses internal NV read only via `ekCertificate()`. | ||
| **Implemented.** | ||
| ```typescript | ||
| // handle: hex string ('0x01c00002') or number | ||
| await tpm.nv.read('0x01c00002'); | ||
| await tpm.nv.read('0x01c00002', 0, 512); | ||
| await tpm.nv.read('0x01800001', 0, undefined, authBuffer); | ||
| ``` | ||
| **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. | ||
| **Safe indices on consumer hardware:** | ||
| | Index | Typical use | Writable | | ||
| |-------|-------------|----------| | ||
| | `0x01c00002`, `0x01c0000A` | EK certificate (RSA / ECC) | **Read-only** (firmware) | | ||
| | `0x01c0000B` | EK template | Read-only | | ||
| | User-defined (`0x01800001`+) | Application data | **`nv.define` then read/write** | | ||
| 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. | ||
| **Flat equivalent:** [`Tpm.nvRead(handle, offset?, size?, auth?)`](#tpm-nvread). | ||
| --- | ||
| ### `tpm.nv.write(handle, data, offset?)` | ||
| ### `tpm.nv.readPublic(handle): Promise<{ dataSize, attributes }>` | ||
| **NOT_SUPPORTED** — throws `NOT_SUPPORTED`. Planned Phase 4. | ||
| **Implemented.** Returns NV index metadata from `NV_ReadPublic` without reading data. | ||
| **Flat equivalent:** [`Tpm.nvReadPublic(handle)`](#tpm-nvreadpublic). | ||
| --- | ||
| ### `tpm.nv.define(opts): Promise<void>` | ||
| **Implemented.** Creates an owner NV index (`TPM2_NV_DefineSpace`). | ||
| ```typescript | ||
| type NvDefineOptions = { | ||
| handle: string | number; // 0x01800000..0x01BFFFFF | ||
| size: number; // 1..65535 bytes | ||
| auth?: Buffer; // index password (if using AUTH* attributes) | ||
| ownerAuth?: Buffer; // owner hierarchy password (often empty) | ||
| }; | ||
| ``` | ||
| **Default attributes:** `OWNERREAD | OWNERWRITE | NO_DA` — read/write via owner auth on `TPM_RH_OWNER`. | ||
| **Destructive / privileged:** Consumes TPM NV space until [`tpm.nv.undefine`](#tpmnvundefinehandle-ownerauth). Refuses EK cert indices. **Not for production laptops without intent.** | ||
| **Flat equivalent:** [`Tpm.nvDefine(opts)`](#tpm-nvdefine). | ||
| --- | ||
| ### `tpm.nv.undefine(handle, ownerAuth?): Promise<void>` | ||
| **Implemented.** Deletes an owner NV index (`TPM2_NV_UndefineSpace`). | ||
| **Flat equivalent:** [`Tpm.nvUndefine(handle, ownerAuth?)`](#tpm-nvundefine). | ||
| --- | ||
| ### `tpm.nv.write(handle, data, offset?, auth?)` | ||
| **Implemented.** | ||
| **Under the hood:** `NV_ReadPublic` bounds check → `NV_Write` with owner or index auth (`TPMA_NV_PPWRITE` / `TPMA_NV_AUTHWRITE`). | ||
| **Caveats:** Most factory NV indices are read-only. User-defined indices must be created with [`tpm.nv.define`](#tpmnvdefineopts-promisevoid) first. | ||
| **Flat equivalent:** [`Tpm.nvWrite(handle, data, offset?, auth?)`](#tpm-nvwrite). | ||
| --- | ||
| ### `tpm.keys.create(opts): Promise<KeyHandle>` | ||
@@ -417,3 +485,3 @@ | ||
| sign?: boolean; // default true | ||
| decrypt?: boolean; // default false; RSA only when implemented | ||
| decrypt?: boolean; // default false; RSA only | ||
| }; | ||
@@ -456,4 +524,29 @@ ``` | ||
| **NOT_SUPPORTED** — throws `NOT_SUPPORTED`. Planned Phase 5. | ||
| **Implemented.** | ||
| ```typescript | ||
| type SealOptions = { | ||
| data: Buffer; | ||
| pcrSelection?: number[]; // SHA-256 bank; binds to current PCR values at seal time | ||
| }; | ||
| const sealed = await tpm.seal.seal({ data: secret }); | ||
| const plain = await tpm.seal.unseal(sealed); | ||
| // PCR-bound: unseal fails if PCR state changes | ||
| const bound = await tpm.seal.seal({ data: secret, pcrSelection: [7] }); | ||
| ``` | ||
| **Under the hood:** | ||
| 1. `CreatePrimary` — transient storage primary. | ||
| 2. Optional `PolicyPCR` session when `pcrSelection` is set; policy digest embedded in sealed object. | ||
| 3. `Create` — keyedhash sealed object (`fixedTPM | fixedParent | userWithAuth | noDA`). | ||
| 4. Export `SEAL` wire blob (public + private + PCR metadata). | ||
| 5. `unseal`: load + `Unseal` (with matching `PolicyPCR` when bound). | ||
| **Caveats:** PCR-bound seal requires the chosen PCRs to match at unseal time. `tpm.pcr.extend` on Windows needs elevation for many indices. | ||
| **Flat equivalents:** [`Tpm.seal(opts)`](#tpm-sealopts), [`Tpm.unseal(blob)`](#tpm-unsealblob). | ||
| --- | ||
@@ -469,3 +562,3 @@ | ||
| **Not for:** General NV index access ([planned](#tpm-nv)). | ||
| **Not for:** Attestation quotes (use `attest.provisionAk`). For general NV access use [`tpm.nv.read`](#tpmnvreadhandle-offset-size-auth). | ||
@@ -569,4 +662,10 @@ --- | ||
| **NOT_SUPPORTED** — throws `NOT_SUPPORTED`. RSA OAEP decrypt planned for keys created with `decrypt: true`. | ||
| **Implemented.** RSA OAEP (SHA-256) via `TPM2_RSA_Decrypt`. Key must have been created with `decrypt: true` (RSA only). | ||
| **Under the hood:** Regenerate storage primary → `Load` → `RSA_Decrypt` with explicit OAEP scheme → flush. | ||
| **Use for:** Decrypting ciphertext produced for the TPM RSA key's public half. | ||
| **Not for:** ECC keys or sign-only RSA keys (`INVALID_ARGUMENT`). | ||
| --- | ||
@@ -738,3 +837,3 @@ | ||
| | `COMMAND_BLOCKED` | Windows TBS driver blocked command ordinal | | ||
| | `NOT_SUPPORTED` | Unimplemented JS stub or PCP/TBS capability gap | | ||
| | `NOT_SUPPORTED` | PCP/TBS capability gap on this platform | — | sometimes | — | | ||
| | `INVALID_ARGUMENT` | Bad options (wrong digest size, invalid key type, empty `keyName`) | | ||
@@ -774,4 +873,8 @@ | `KEY_NOT_FOUND` | NCrypt persisted key missing | | ||
| | `Tpm.isAvailable()`, `open()`, `info()` | ✓ | ✓ | ✓ | | ||
| | `tpm.random.bytes`, `tpm.pcr.read`, `tpm.pcr.extend` | ✓ | ✓ | ✓ | | ||
| | `tpm.keys.create/load`, `key.sign` | ✓ | ✓ | ✓ | | ||
| | `tpm.random.bytes`, `tpm.pcr.read` | ✓ | ✓ | ✓ | | ||
| | `tpm.pcr.extend` | ✓ † | ✗ → `REQUIRES_ELEVATION` | ✓ † | | ||
| | `tpm.nv.read` / `tpm.nv.write` | ✓ ‡ | ✓ ‡ | ✓ | | ||
| | `tpm.nv.define` / `tpm.nv.undefine` | ✓ § | ✓ § | ✓ § | | ||
| | `tpm.keys.create/load`, `key.sign`, `key.decrypt` | ✓ | ✓ | ✓ | | ||
| | `tpm.seal.seal` / `tpm.seal.unseal` | ✓ | ✓ | ✓ | | ||
| | `tpm.attest.provisionAk()` user scope | ✓ | ✓ | ✓ | | ||
@@ -781,7 +884,9 @@ | `tpm.attest.provisionAk({ scope: 'machine' })` | — | ✗ | ✓ | | ||
| | `ak.activateCredential` | ✓ | ✗ | ✓ | | ||
| | `tpm.pcr.extend` | ✓* | ✓* | ✓ | | ||
| | `tpm.nv.*` (planned) | ✓* | ✓* | ✓ | | ||
| \* Planned; firmware/policy may still deny. | ||
| † **`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). | ||
| ‡ **`nv.read/write`:** Index permissions vary; EK cert indices are read-only. Writes to undefined indices fail at the TPM. | ||
| § **`nv.define/undefine`:** Owner authorization required; owner NV range only. Destructive on NV space. | ||
| Linux requires read/write on `/dev/tpmrm0`. Windows fleet pattern: provision machine AK once elevated → standard users quote forever. | ||
@@ -791,14 +896,8 @@ | ||
| ## Planned / not yet implemented | ||
| ## Deferred / not in public API | ||
| These methods exist on `TpmHandle` but **throw `TpmError` with code `NOT_SUPPORTED`** at call time: | ||
| | Feature | Notes | | ||
| |---------|-------| | ||
| | *(none — all planned namespaces are implemented)* | See [roadmap](./roadmap.md) for hardening / polish | | ||
| | Method | Phase | Notes | | ||
| |--------|-------|-------| | ||
| | `tpm.nv.read/write` | 4 | General NV; EK cert uses internal path today | | ||
| | `tpm.seal.seal` / `tpm.seal.unseal` | 5 | PCR-bound sealed storage | | ||
| | `key.decrypt(cipher)` | 2+ | RSA OAEP for decrypt keys | | ||
| Roadmap detail: [roadmap.md](./roadmap.md). | ||
| --- | ||
@@ -815,8 +914,12 @@ | ||
| | PCR extend | `tpm.pcr.extend(i, d)` | `Tpm.pcrExtend(i, d)` | `void` | | ||
| | NV | `tpm.nv.read/write` | — | **NOT_SUPPORTED** | | ||
| | NV read | `tpm.nv.read(h, off?, sz?, auth?)` | `Tpm.nvRead(...)` | `Buffer` | | ||
| | NV write | `tpm.nv.write(h, data, off?, auth?)` | `Tpm.nvWrite(...)` | `void` | | ||
| | NV readPublic | `tpm.nv.readPublic(h)` | `Tpm.nvReadPublic(h)` | `{ dataSize, attributes }` | | ||
| | NV define | `tpm.nv.define(opts)` | `Tpm.nvDefine(opts)` | `void` | | ||
| | NV undefine | `tpm.nv.undefine(h, ownerAuth?)` | `Tpm.nvUndefine(...)` | `void` | | ||
| | Create key | `tpm.keys.create(opts)` | `Tpm.createKey(opts)` | `KeyHandle` / `{ publicKeyDer, keyBlob }` | | ||
| | Load key | `tpm.keys.load(blob)` | — | `KeyHandle` | | ||
| | Sign | `key.sign(digest)` | `Tpm.signKeyBlob({ keyBlob, digest })` | `Buffer` | | ||
| | Decrypt | `key.decrypt(cipher)` | — | **NOT_SUPPORTED** | | ||
| | Seal | `tpm.seal.seal/unseal` | — | **NOT_SUPPORTED** | | ||
| | Decrypt | `key.decrypt(cipher)` | `Tpm.decryptKeyBlob({ keyBlob, cipher })` | `Buffer` | | ||
| | Seal | `tpm.seal.seal/unseal` | `Tpm.seal` / `Tpm.unseal` | `Buffer` | | ||
| | EK cert | `tpm.attest.ekCertificate()` | `Tpm.readEkCertificate()` | `Buffer \| null` | | ||
@@ -902,2 +1005,10 @@ | Provision AK | `tpm.attest.provisionAk(opts)` | `Tpm.provisionAk(opts)` | `AkHandle` / `{ akPublicDer, akBlob }` | | ||
| | `Tpm.signKeyBlob` | function | Implemented | | ||
| | `Tpm.decryptKeyBlob` | function | Implemented | | ||
| | `Tpm.nvRead` | function | Implemented | | ||
| | `Tpm.nvWrite` | function | Implemented | | ||
| | `Tpm.nvReadPublic` | function | Implemented | | ||
| | `Tpm.nvDefine` | function | Implemented | | ||
| | `Tpm.nvUndefine` | function | Implemented | | ||
| | `Tpm.seal` | function | Implemented | | ||
| | `Tpm.unseal` | function | Implemented | | ||
| | `TpmHandle.info` | method | Implemented | | ||
@@ -908,8 +1019,11 @@ | `TpmHandle.readPublic` | method | Implemented | | ||
| | `TpmHandle.random.bytes` | method | Implemented | | ||
| | `TpmHandle.nv.read` | method | **NOT_SUPPORTED** | | ||
| | `TpmHandle.nv.write` | method | **NOT_SUPPORTED** | | ||
| | `TpmHandle.nv.read` | method | Implemented | | ||
| | `TpmHandle.nv.write` | method | Implemented | | ||
| | `TpmHandle.nv.readPublic` | method | Implemented | | ||
| | `TpmHandle.nv.define` | method | Implemented | | ||
| | `TpmHandle.nv.undefine` | method | Implemented | | ||
| | `TpmHandle.keys.create` | method | Implemented | | ||
| | `TpmHandle.keys.load` | method | Implemented | | ||
| | `TpmHandle.seal.seal` | method | **NOT_SUPPORTED** | | ||
| | `TpmHandle.seal.unseal` | method | **NOT_SUPPORTED** | | ||
| | `TpmHandle.seal.seal` | method | Implemented | | ||
| | `TpmHandle.seal.unseal` | method | Implemented | | ||
| | `TpmHandle.attest.ekCertificate` | method | Implemented | | ||
@@ -922,3 +1036,3 @@ | `TpmHandle.attest.provisionAk` | method | Implemented | | ||
| | `KeyHandle.sign` | method | Implemented | | ||
| | `KeyHandle.decrypt` | method | **NOT_SUPPORTED** | | ||
| | `KeyHandle.decrypt` | method | Implemented | | ||
| | `AkHandle.export` | method | Implemented | | ||
@@ -925,0 +1039,0 @@ | `AkHandle.publicKeyDer` | getter | Implemented | |
+28
-32
@@ -12,22 +12,20 @@ # API roadmap | ||
| ## Current state (0.0.4-beta) | ||
| ## Current state (0.0.5-beta) | ||
| **Shipped** | ||
| **Shipped and validated on real Windows 11 hardware (Intel TPM, non-virtual):** attestation (user + machine provision, cross-user quote, SYSTEM provision), `random`, `keys` (sign + RSA decrypt), `pcr.read` / `pcr.extend` (admin on Windows), `nv` (read/write/define/undefine/readPublic), `seal` / `unseal`. | ||
| | Namespace | Methods | | ||
| |-----------|---------| | ||
| | Root | `Tpm.isAvailable()`, `Tpm.open()`, `Tpm.info()` | | ||
| | Root | `Tpm.isAvailable()`, `Tpm.open()`, `Tpm.info()`, `tpm.readPublic()` | | ||
| | `tpm.random` | `bytes(n)` | | ||
| | `tpm.pcr` | `read`, `extend` | | ||
| | `tpm.nv` | `read`, `write`, `readPublic`, `define`, `undefine` | | ||
| | `tpm.keys` | `create`, `load`; `KeyHandle.sign`, `export`, `decrypt` | | ||
| | `tpm.seal` | `seal`, `unseal` | | ||
| | `tpm.attest` | `provisionAk`, `quote`, `ekCertificate` | | ||
| | `AkHandle` | `export`, `quote`, `activateCredential`, `publicKeyDer` | | ||
| | Flat | `Tpm.pcrRead`, `Tpm.pcrExtend`, `readPublic`, `readEkCertificate`, `quote`, `provisionAk`, `activateCredential` | | ||
| | Flat | Parity wrappers for all of the above (`Tpm.pcrRead`, `Tpm.nvDefine`, `Tpm.seal`, …) | | ||
| **Rust foundation already present (not exposed on `TpmHandle` yet):** | ||
| **Platform split:** General ops (keys, seal, NV, PCR, random) use TBS on both OSes. Attestation persistence on Windows uses NCrypt PCP (`PCP1` / `PCP2` blobs); Linux uses TBS-wrapped ECDSA AK blobs. | ||
| - Command codec: `CreatePrimary`, `Create`, `Load`, `FlushContext`, `Quote`, `GetRandom`, sessions, policy digest | ||
| - Linux key path: `keys.rs` (storage primary, AK create/load) | ||
| - Windows PCP path: `pcp.rs` (identity AK, machine DACL, quote, activation) | ||
| - NV: EK certificate read via fixed index | ||
| - Credential: full activate-credential flow (Linux TBS; Windows PCP) | ||
| --- | ||
@@ -125,3 +123,3 @@ | ||
| ### Phase 2 — `tpm.keys` ✅ (this branch; decrypt deferred) | ||
| ### Phase 2 — `tpm.keys` ✅ | ||
@@ -132,3 +130,3 @@ **Goal:** General exportable signing keys via TBS wrapped blobs (both OSes). | ||
| - [x] Unit tests: templates, Sign command golden, option validation, HW roundtrip | ||
| - [ ] `key.decrypt` — RSA OAEP (deferred) | ||
| - [x] `key.decrypt` — RSA OAEP | ||
| - [ ] Windows VM sign smoke | ||
@@ -144,28 +142,26 @@ | ||
| - [x] Caveats: some firmware policies lock PCRs; surface `TPM_RC` / `COMMAND_BLOCKED` cleanly. | ||
| - [ ] Acceptance: works unprivileged on swtpm and dev VM where PCRs are extendable. | ||
| - [x] Acceptance: Linux unprivileged on swtpm/dev VM; **Windows Administrator** on real client hardware (PCR 23 validated). | ||
| ### Phase 4 — `tpm.nv` (1 week) | ||
| ### Phase 4 — `tpm.nv` ✅ (this branch) | ||
| **Goal:** General NV index access beyond EK cert helper. | ||
| - Rust: | ||
| - `nv.read_public(handle)` — already partially in `nv.rs`; expose metadata (size, attributes). | ||
| - `nv.read(handle, offset, size)`. | ||
| - `nv.write(handle, offset, data, auth?)` — auth optional buffer for password/session auth. | ||
| - `nv.define` / `nv.undefine` — **defer** unless needed (owner-auth, high privilege). | ||
| - Migrate `readEkCertificate` to call `nv.read` on well-known EK cert index internally. | ||
| - JS: `tpm.nv.read`, `tpm.nv.write`; document which indices are safe on consumer hardware. | ||
| - Acceptance: EK cert read unchanged; optional integration test against swtpm-defined NV index. | ||
| - [x] Rust: `nv.read_public(handle)` — size + attributes via `nv_read_public`. | ||
| - [x] `nv.read(handle, offset, size)`. | ||
| - [x] `nv.write(handle, offset, data, auth?)` — optional auth for password-protected indices. | ||
| - [x] `nv.define` / `nv.undefine` — owner-auth; owner NV range only; refuses EK indices. | ||
| - [x] Migrate `readEkCertificate` to call `nv.read` on well-known EK cert index internally. | ||
| - [x] JS: `tpm.nv.read`, `tpm.nv.write`, `tpm.nv.readPublic`, `tpm.nv.define`, `tpm.nv.undefine`; document which indices are safe on consumer hardware. | ||
| - [ ] Acceptance: EK cert read unchanged; optional integration test against swtpm-defined NV index (hardware: use `examples/nv-smoke.mjs` on test machine). | ||
| ### Phase 5 — `tpm.seal` / `tpm.unseal` (1–2 weeks) | ||
| ### Phase 5 — `tpm.seal` / `tpm.unseal` ✅ (this branch) | ||
| **Goal:** TPM-bound secrets with optional PCR policy. | ||
| - Rust: | ||
| - `seal({ data, pcrSelection?, name? })` — create storage primary or use fixed template, `Create` sealed object, export blob. | ||
| - `unseal(blob)` — load + `Unseal`. | ||
| - PCR policy: `PolicyPCR` session when `pcrSelection` provided. | ||
| - JS: `tpm.seal`, `tpm.unseal`; flat aliases. | ||
| - Tests: roundtrip without PCR; roundtrip with PCR extend before unseal; negative test wrong PCR. | ||
| - Acceptance: Linux + Windows TBS; document that PCR-bound seal requires extend permission on chosen indices. | ||
| - [x] Rust: `seal({ data, pcrSelection? })` — storage primary, `Create` sealed object, export blob. | ||
| - [x] `unseal(blob)` — load + `Unseal`. | ||
| - [x] PCR policy: `PolicyPCR` session when `pcrSelection` provided. | ||
| - [x] JS: `tpm.seal`, `tpm.unseal`; flat aliases. | ||
| - [x] Tests: roundtrip without PCR; unit tests for marshalling. | ||
| - [ ] Acceptance: Linux + Windows TBS; roundtrip with PCR extend before unseal on hardware. | ||
@@ -223,3 +219,3 @@ ### Phase 6 — Hardening & 1.0 (ongoing) | ||
| - Implement phases on `dev`; beta publish after each phase or logical group (e.g. beta.4 = random + keys). | ||
| - Implement phases on `dev`; beta publish after each phase or logical group (e.g. **0.0.5-beta.0** = full NV + seal + keys decrypt). | ||
| - `1.0.0` when Phases 0–5 acceptance criteria pass on real hardware and API surface in README matches implementation. |
@@ -10,2 +10,3 @@ # Windows: Platform Crypto Provider (PCP) | ||
| | `Tpm.isAvailable()`, PCR read, `readPublic` | Yes | Yes | Yes | | ||
| | `tpm.pcr.extend` | No (`REQUIRES_ELEVATION`) | Yes † | Yes † | | ||
| | `provisionAk()` user scope (`PCP1`) | Yes | Yes | Yes | | ||
@@ -18,2 +19,4 @@ | `quote()` | Yes | Yes | Yes | | ||
| **† `pcr.extend`:** Prefer PCR indices **16–23** (not **0–7**, which are boot/Secure Boot measurements). Standard users receive **`REQUIRES_ELEVATION`** (`hresult` `0x80280400`); **Administrator** can extend (validated on real Intel laptop). Linux standard user can extend unless firmware locks the index. | ||
| ## AK blob formats | ||
@@ -20,0 +23,0 @@ |
+50
-8
@@ -87,3 +87,3 @@ export declare class TpmError extends Error { | ||
| /** @throws {TpmError} NOT_SUPPORTED until Phase 5 */ | ||
| /** Sealed blob options. */ | ||
| export declare type SealOptions = { | ||
@@ -94,2 +94,17 @@ data: Buffer; | ||
| /** Owner NV index definition (requires owner authorization). */ | ||
| export declare type NvDefineOptions = { | ||
| handle: string | number; | ||
| size: number; | ||
| /** Index password when attributes use AUTHREAD/AUTHWRITE. */ | ||
| auth?: Buffer; | ||
| /** Owner hierarchy password (often empty on consumer TPMs). */ | ||
| ownerAuth?: Buffer; | ||
| }; | ||
| export declare type NvReadPublicResult = { | ||
| dataSize: number; | ||
| attributes: number; | ||
| }; | ||
| export declare interface AkHandle { | ||
@@ -102,3 +117,3 @@ export(): AkBlob; | ||
| /** @throws {TpmError} NOT_SUPPORTED until RSA decrypt is implemented */ | ||
| /** @throws {TpmError} when key lacks decrypt attribute */ | ||
| export declare interface KeyHandle { | ||
@@ -127,6 +142,17 @@ export(): KeyBlob; | ||
| nv: { | ||
| /** @throws {TpmError} NOT_SUPPORTED until Phase 4 */ | ||
| read(handle: string, offset?: number, size?: number): Promise<Buffer>; | ||
| /** @throws {TpmError} NOT_SUPPORTED until Phase 4 */ | ||
| write(handle: string, data: Buffer, offset?: number): Promise<void>; | ||
| readPublic(handle: string | number): Promise<NvReadPublicResult>; | ||
| read( | ||
| handle: string | number, | ||
| offset?: number, | ||
| size?: number, | ||
| auth?: Buffer, | ||
| ): Promise<Buffer>; | ||
| write( | ||
| handle: string | number, | ||
| data: Buffer, | ||
| offset?: number, | ||
| auth?: Buffer, | ||
| ): Promise<void>; | ||
| define(opts: NvDefineOptions): Promise<void>; | ||
| undefine(handle: string | number, ownerAuth?: Buffer): Promise<void>; | ||
| }; | ||
@@ -138,5 +164,3 @@ keys: { | ||
| seal: { | ||
| /** @throws {TpmError} NOT_SUPPORTED until Phase 5 */ | ||
| seal(opts: SealOptions): Promise<Buffer>; | ||
| /** @throws {TpmError} NOT_SUPPORTED until Phase 5 */ | ||
| unseal(blob: Buffer): Promise<Buffer>; | ||
@@ -168,2 +192,20 @@ }; | ||
| signKeyBlob(opts: { keyBlob: KeyBlob; digest: Buffer }): Promise<Buffer>; | ||
| decryptKeyBlob(opts: { keyBlob: KeyBlob; cipher: Buffer }): Promise<Buffer>; | ||
| nvRead( | ||
| handle: string | number, | ||
| offset?: number, | ||
| size?: number, | ||
| auth?: Buffer, | ||
| ): Promise<Buffer>; | ||
| nvWrite( | ||
| handle: string | number, | ||
| data: Buffer, | ||
| offset?: number, | ||
| auth?: Buffer, | ||
| ): Promise<void>; | ||
| nvReadPublic(handle: string | number): Promise<NvReadPublicResult>; | ||
| nvDefine(opts: NvDefineOptions): Promise<void>; | ||
| nvUndefine(handle: string | number, ownerAuth?: Buffer): Promise<void>; | ||
| seal(opts: SealOptions): Promise<Buffer>; | ||
| unseal(blob: Buffer): Promise<Buffer>; | ||
| }; |
+60
-52
@@ -80,4 +80,4 @@ // prettier-ignore | ||
| const bindingPackageVersion = require('node-tpm2-android-arm64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -97,4 +97,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-android-arm-eabi/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -119,4 +119,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-win32-x64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -136,4 +136,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-windows-x64-msvc/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -154,4 +154,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-win32-ia32-msvc/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -171,4 +171,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-windows-arm64-msvc/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -191,4 +191,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-darwin-universal/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -208,4 +208,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-darwin-x64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -225,4 +225,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-darwin-arm64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -246,4 +246,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-freebsd-x64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -263,4 +263,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-freebsd-arm64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -285,4 +285,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-x64-musl/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -302,4 +302,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-x64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -321,4 +321,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-arm64-musl/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -338,4 +338,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-arm64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -357,4 +357,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-arm-musleabihf/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -374,4 +374,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-arm-gnueabihf/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -393,4 +393,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-loong64-musl/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -410,4 +410,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-loong64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -429,4 +429,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-riscv64-musl/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -446,4 +446,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-riscv64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -464,4 +464,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-ppc64-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -481,4 +481,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-linux-s390x-gnu/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -502,4 +502,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-openharmony-arm64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -519,4 +519,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-openharmony-x64/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -536,4 +536,4 @@ return binding | ||
| const bindingPackageVersion = require('node-tpm2-openharmony-arm/package.json').version | ||
| if (bindingPackageVersion !== '0.0.4-beta.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.4-beta.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| if (bindingPackageVersion !== '0.0.5-beta.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { | ||
| throw new Error(`Native binding package version mismatch, expected 0.0.5-beta.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) | ||
| } | ||
@@ -618,5 +618,11 @@ return binding | ||
| module.exports.createKey = nativeBinding.createKey | ||
| module.exports.decryptKeyBlob = nativeBinding.decryptKeyBlob | ||
| module.exports.getFixedProperties = nativeBinding.getFixedProperties | ||
| module.exports.isAvailable = nativeBinding.isAvailable | ||
| module.exports.keyBlobPublicDer = nativeBinding.keyBlobPublicDer | ||
| module.exports.nvDefine = nativeBinding.nvDefine | ||
| module.exports.nvRead = nativeBinding.nvRead | ||
| module.exports.nvReadPublic = nativeBinding.nvReadPublic | ||
| module.exports.nvUndefine = nativeBinding.nvUndefine | ||
| module.exports.nvWrite = nativeBinding.nvWrite | ||
| module.exports.pcrExtend = nativeBinding.pcrExtend | ||
@@ -629,2 +635,4 @@ module.exports.pcrRead = nativeBinding.pcrRead | ||
| module.exports.readPublic = nativeBinding.readPublic | ||
| module.exports.seal = nativeBinding.seal | ||
| module.exports.signKeyBlob = nativeBinding.signKeyBlob | ||
| module.exports.unseal = nativeBinding.unseal |
+50
-0
@@ -18,2 +18,9 @@ /* auto-generated by NAPI-RS */ | ||
| export declare function decryptKeyBlob(opts: DecryptKeyBlobOptionsJs): Promise<Buffer> | ||
| export interface DecryptKeyBlobOptionsJs { | ||
| keyBlob: AkBlobJs | ||
| cipher: Buffer | ||
| } | ||
| export interface FixedPropertiesJs { | ||
@@ -43,2 +50,36 @@ manufacturer: string | ||
| export declare function nvDefine(opts: NvDefineOptionsJs): Promise<void> | ||
| export interface NvDefineOptionsJs { | ||
| handle: string | ||
| size: number | ||
| auth?: Buffer | ||
| ownerAuth?: Buffer | ||
| } | ||
| export declare function nvRead(handle: string, offset?: number | undefined | null, size?: number | undefined | null, auth?: Buffer | undefined | null): Promise<Buffer> | ||
| export declare function nvReadPublic(handle: string): Promise<NvReadPublicJs> | ||
| export interface NvReadPublicJs { | ||
| dataSize: number | ||
| attributes: number | ||
| } | ||
| export declare function nvUndefine(opts: NvUndefineOptionsJs): Promise<void> | ||
| export interface NvUndefineOptionsJs { | ||
| handle: string | ||
| ownerAuth?: Buffer | ||
| } | ||
| export declare function nvWrite(opts: NvWriteOptionsJs): Promise<void> | ||
| export interface NvWriteOptionsJs { | ||
| handle: string | ||
| data: Buffer | ||
| offset?: number | ||
| auth?: Buffer | ||
| } | ||
| export declare function pcrExtend(index: number, digest: Buffer): Promise<void> | ||
@@ -88,2 +129,9 @@ | ||
| export declare function seal(opts: SealOptionsJs): Promise<Buffer> | ||
| export interface SealOptionsJs { | ||
| data: Buffer | ||
| pcrSelection?: Array<number> | ||
| } | ||
| export declare function signKeyBlob(opts: SignKeyBlobOptionsJs): Promise<Buffer> | ||
@@ -95,1 +143,3 @@ | ||
| } | ||
| export declare function unseal(blob: Buffer): Promise<Buffer> |
+10
-10
| { | ||
| "name": "node-tpm2", | ||
| "version": "0.0.4-beta.4", | ||
| "version": "0.0.5-beta.0", | ||
| "description": "TPM 2.0 attestation for Node.js — prebuilt native bindings, PCR quotes, and fleet-ready Windows PCP keys. No tpm2-tools.", | ||
@@ -84,12 +84,12 @@ "type": "module", | ||
| "optionalDependencies": { | ||
| "node-tpm2-windows-x64-msvc": "0.0.4-beta.4", | ||
| "node-tpm2-windows-arm64-msvc": "0.0.4-beta.4", | ||
| "node-tpm2-linux-x64-gnu": "0.0.4-beta.4", | ||
| "node-tpm2-linux-arm64-gnu": "0.0.4-beta.4", | ||
| "node-tpm2-linux-x64-musl": "0.0.4-beta.4", | ||
| "node-tpm2-linux-arm64-musl": "0.0.4-beta.4", | ||
| "node-tpm2-darwin-arm64": "0.0.4-beta.4", | ||
| "node-tpm2-win32-x64-msvc": "0.0.4-beta.4", | ||
| "node-tpm2-win32-arm64-msvc": "0.0.4-beta.4" | ||
| "node-tpm2-windows-x64-msvc": "0.0.5-beta.0", | ||
| "node-tpm2-windows-arm64-msvc": "0.0.5-beta.0", | ||
| "node-tpm2-linux-x64-gnu": "0.0.5-beta.0", | ||
| "node-tpm2-linux-arm64-gnu": "0.0.5-beta.0", | ||
| "node-tpm2-linux-x64-musl": "0.0.5-beta.0", | ||
| "node-tpm2-linux-arm64-musl": "0.0.5-beta.0", | ||
| "node-tpm2-darwin-arm64": "0.0.5-beta.0", | ||
| "node-tpm2-win32-x64-msvc": "0.0.5-beta.0", | ||
| "node-tpm2-win32-arm64-msvc": "0.0.5-beta.0" | ||
| } | ||
| } |
+52
-17
@@ -21,3 +21,3 @@ # node-tpm2 | ||
| **Pre-release** (`0.0.x-beta`). [Roadmap](./docs/roadmap.md) for remaining namespaces. | ||
| **Pre-release** (`0.0.x-beta`). Full public API implemented; validated on real Windows 11 + Intel TPM. [API reference](./docs/api-reference.md) · [Roadmap](./docs/roadmap.md). | ||
@@ -198,6 +198,8 @@ --- | ||
| | `tpm.pcr.read(...)` | ✓ | ✓ | ✓ | | ||
| | `tpm.pcr.extend(i, digest)` | ✓ * | ✓ * | ✓ | | ||
| | `tpm.pcr.extend(i, digest)` | ✓ † | ✗ → `REQUIRES_ELEVATION` | ✓ † | | ||
| | **nv** | | | | | ||
| | `tpm.nv.read(...)` | ✓ *planned* | ✓ *planned* | ✓ | | ||
| | `tpm.nv.write(...)` | ✓ *planned* | ✓ *planned* | ✓ | | ||
| | `tpm.nv.read(...)` | ✓ ‡ | ✓ ‡ | ✓ | | ||
| | `tpm.nv.write(...)` | ✓ ‡ | ✓ ‡ | ✓ | | ||
| | `tpm.nv.define(...)` | ✓ § | ✓ § | ✓ § | | ||
| | `tpm.nv.undefine(...)` | ✓ § | ✓ § | ✓ § | | ||
| | `tpm.attest.ekCertificate()` | ✓ | ✓ | ✓ | | ||
@@ -208,6 +210,6 @@ | **keys** | | | | | ||
| | `key.sign(digest)` | ✓ | ✓ | ✓ | | ||
| | `key.decrypt(cipher)` | — *planned* | — *planned* | — *planned* | | ||
| | `key.decrypt(cipher)` | ✓ | ✓ | ✓ | | ||
| | **seal** | | | | | ||
| | `tpm.seal(...)` | ✓ *planned* | ✓ *planned* | ✓ | | ||
| | `tpm.unseal(blob)` | ✓ *planned* | ✓ *planned* | ✓ | | ||
| | `tpm.seal.seal(...)` | ✓ | ✓ | ✓ | | ||
| | `tpm.unseal(blob)` | ✓ | ✓ | ✓ | | ||
| | **attest** | | | | | ||
@@ -223,4 +225,10 @@ | `tpm.attest.provisionAk()` user | ✓ | ✓ | ✓ | | ||
| **Planned rows** are design targets from the [roadmap](./docs/roadmap.md); unprivileged use matches the Phase 0 spike (`GetRandom`, `CreatePrimary` succeeded on Windows 11 without admin). Firmware or group policy can still deny specific PCR/NV operations — those surface as `TPM_RC` or `COMMAND_BLOCKED`, not silent failure. | ||
| **Hardware validation (beta):** Windows 11 Intel TPM — attestation suite, `random`, `keys`, `pcr.read` / `pcr.extend` (elevated), `nv.read`. Linux: CI + swtpm. Firmware or group policy can still deny specific PCR/NV operations — those surface as `TPM_RC` or `REQUIRES_ELEVATION`, not silent failure. | ||
| **‡ `nv.read/write`:** Success depends on index attributes and auth. EK cert indices (`0x01c00002`, `0x01c0000A`) are read-only. | ||
| **§ `nv.define/undefine`:** Owner NV range only (`0x01800000`–`0x01BFFFFF`). Requires owner authorization (often empty password). **Consumes NV space** until undefined — use only on test machines or with a chosen index. | ||
| **† `pcr.extend`:** Linux standard user (prefer indices **16–23** for experiments; avoid **0–7** boot/Secure Boot PCRs). **Windows standard user → `REQUIRES_ELEVATION`** (`TPM_E_COMMAND_BLOCKED` from TBS). Windows Administrator can extend on real hardware (validated). Standard-user failure is not `COMMAND_BLOCKED` — re-run elevated. | ||
| --- | ||
@@ -275,4 +283,28 @@ | ||
| Flat: `Tpm.createKey()`, `Tpm.signKeyBlob({ keyBlob, digest })`. RSA `decrypt` is not yet implemented. | ||
| const rsaKey = await tpm.keys.create({ type: 'rsa', sign: true, decrypt: true }); | ||
| const plain = await rsaKey.decrypt(ciphertext); | ||
| Flat: `Tpm.createKey()`, `Tpm.signKeyBlob({ keyBlob, digest })`, `Tpm.decryptKeyBlob({ keyBlob, cipher })`. | ||
| ### NV | ||
| ```javascript | ||
| await tpm.nv.read('0x01c00002'); // EK cert index (read-only on most hardware) | ||
| await tpm.nv.readPublic('0x01800042'); // metadata before read/write | ||
| await tpm.nv.define({ handle: '0x01800042', size: 64 }); // owner NV — test machines only | ||
| await tpm.nv.write('0x01800042', data, 0); | ||
| await tpm.nv.undefine('0x01800042'); | ||
| ``` | ||
| Flat: `Tpm.nvRead`, `Tpm.nvWrite`, `Tpm.nvReadPublic`, `Tpm.nvDefine`, `Tpm.nvUndefine`. See `examples/nv-smoke.mjs`. | ||
| ### Seal | ||
| ```javascript | ||
| const sealed = await tpm.seal.seal({ data: secret, pcrSelection: [23] }); | ||
| const plain = await tpm.seal.unseal(sealed); | ||
| ``` | ||
| Flat: `Tpm.seal`, `Tpm.unseal`. | ||
| ### Attestation | ||
@@ -355,4 +387,4 @@ | ||
| | `ACCESS_DENIED` | OS denied device or key access | — | sometimes | Linux: `tss` group; container: pass device | | ||
| | `REQUIRES_ELEVATION` | Windows operation needs Admin/SYSTEM | — | ✓ | Re-run enrollment elevated or as SYSTEM | | ||
| | `COMMAND_BLOCKED` | Windows TBS driver blocked the command ordinal | ✓ | — | Use NCrypt PCP path (e.g. activation) | | ||
| | `REQUIRES_ELEVATION` | Windows operation needs Admin/SYSTEM | — | ✓ | Re-run enrollment elevated or as SYSTEM; **`pcr.extend` from standard user** | | ||
| | `COMMAND_BLOCKED` | Windows TBS blocked raw ordinal (e.g. ActivateCredential) | ✓ | — | Use NCrypt PCP — elevation does not help | | ||
| | `NOT_SUPPORTED` | Feature or PCP capability missing on this platform | — | sometimes | — | | ||
@@ -376,3 +408,5 @@ | `INVALID_ARGUMENT` | Bad JS/Rust option (e.g. empty machine `keyName`) | — | sometimes | Fix caller input | | ||
| | Format | `(rc & 0xFF00) === 0x0100` or FMT1 bit set | `MARSHALLING_ERROR` | `0x125` (`TPM_RC_ASYMMETRIC`) | | ||
| | Windows TBS blocked | `rc === 0x80280400` | `COMMAND_BLOCKED` | `0x80280400` | | ||
| | Windows TBS blocked | `rc === 0x80280400` | `COMMAND_BLOCKED` * | `0x80280400` | | ||
| \* **`PCR_Extend`:** mapped to **`REQUIRES_ELEVATION`** (same `hresult` `0x80280400`) — Administrator can extend on Windows client; standard user should re-run elevated. | ||
| | Other | everything else | `TPM_RC` | vendor-specific | | ||
@@ -404,5 +438,5 @@ | ||
| ## API reference (planned) | ||
| ## API reference | ||
| Subsystem namespaces not yet on `TpmHandle`. See [docs/roadmap.md](./docs/roadmap.md) for phases and acceptance criteria. | ||
| Subsystem namespaces on `TpmHandle`. See [docs/api-reference.md](./docs/api-reference.md) for full detail. | ||
@@ -412,5 +446,6 @@ | Namespace | Methods | | ||
| | `tpm.random` | `bytes(n)` ✅ | | ||
| | `tpm.keys` | `create`, `load`, `KeyHandle.sign` ✅ · `decrypt` planned | | ||
| | `tpm.pcr` | `extend(index, digest)` | | ||
| | `tpm.seal` | `seal`, `unseal` | | ||
| | `tpm.keys` | `create`, `load`, `KeyHandle.sign`, `KeyHandle.decrypt` ✅ | | ||
| | `tpm.pcr` | `read`, `extend` ✅ | | ||
| | `tpm.nv` | `read`, `write`, `readPublic`, `define`, `undefine` ✅ | | ||
| | `tpm.seal` | `seal`, `unseal` ✅ | | ||
@@ -417,0 +452,0 @@ --- |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
141668
12.7%14
7.69%1538
20.34%474
7.97%62
-1.59%