@vercel/cli-auth 0.3.13 → 0.3.14-snapshot-c4f338842961
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/credentials-store.d.ts +8 -0
- package/credentials-store.js +39 -3
- package/credentials-store.js.map +1 -1
- package/credentials-store.ts +63 -3
- package/hardware-key.d.ts +51 -0
- package/hardware-key.js +184 -0
- package/hardware-key.js.map +1 -0
- package/hardware-key.ts +227 -0
- package/koffi.d.ts +2 -0
- package/koffi.js +8 -0
- package/koffi.js.map +1 -0
- package/koffi.ts +6 -0
- package/linux-tpm.d.ts +30 -0
- package/linux-tpm.js +313 -0
- package/linux-tpm.js.map +1 -0
- package/linux-tpm.ts +371 -0
- package/package.json +6 -2
- package/secure-enclave.d.ts +18 -0
- package/secure-enclave.js +180 -0
- package/secure-enclave.js.map +1 -0
- package/secure-enclave.ts +304 -0
- package/tpm.d.ts +13 -0
- package/tpm.js +160 -0
- package/tpm.js.map +1 -0
- package/tpm.ts +221 -0
package/hardware-key.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// DPoP keys protected by this device's security chip: the Secure Enclave on a
|
|
2
|
+
// Mac, the TPM on Windows and Linux. The DPoP key itself is a software key,
|
|
3
|
+
// and the login saves it only sealed by a key that never leaves the chip, so
|
|
4
|
+
// a copy of the login can't be unsealed, or sign, on any other device.
|
|
5
|
+
//
|
|
6
|
+
// Unsealing asks for nothing (no password, Touch ID, or PIN), so it doesn't
|
|
7
|
+
// protect against code running as the user on this device: that code can
|
|
8
|
+
// have the chip unseal the key, the same way the CLI does, and then use it
|
|
9
|
+
// anywhere. Sealing protects a copied login, not a compromised device.
|
|
10
|
+
//
|
|
11
|
+
// Once unsealed, a key is kept in this process's memory for
|
|
12
|
+
// `DPOP_KEY_CACHE_TTL_MS`, so a command uses the chip once, not for every
|
|
13
|
+
// request (a deploy signs one for every file it uploads). Nothing unsealed
|
|
14
|
+
// outlives the command.
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
createECDH,
|
|
18
|
+
createPrivateKey,
|
|
19
|
+
generateKeyPairSync,
|
|
20
|
+
type KeyObject,
|
|
21
|
+
randomUUID,
|
|
22
|
+
sign,
|
|
23
|
+
} from 'node:crypto';
|
|
24
|
+
import {
|
|
25
|
+
createSecureEnclaveKey,
|
|
26
|
+
unsealWithSecureEnclaveKey,
|
|
27
|
+
} from './secure-enclave';
|
|
28
|
+
import { createTpmKey, deleteTpmKey, unsealWithTpmKey } from './tpm';
|
|
29
|
+
import { hasLinuxTpm, sealWithLinuxTpm, unsealWithLinuxTpm } from './linux-tpm';
|
|
30
|
+
|
|
31
|
+
export { TpmAccessError } from './linux-tpm';
|
|
32
|
+
export { SecureEnclaveLockedError } from './secure-enclave';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* How long an unsealed DPoP key is kept in memory, so the chip isn't used
|
|
36
|
+
* for every request. Fixed from when the key was unsealed, not extended by
|
|
37
|
+
* use, which only matters to long-running commands.
|
|
38
|
+
*/
|
|
39
|
+
export const DPOP_KEY_CACHE_TTL_MS = 30 * 60 * 1000;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A DPoP key's private half (`d`), `sealed` by a key in this device's chip:
|
|
43
|
+
* a Secure Enclave key, in the wrapped form only this Mac's Secure Enclave
|
|
44
|
+
* can use, the name of a key in this PC's TPM, or, on Linux, the public area
|
|
45
|
+
* of a TPM sealed object whose private area is `sealed`.
|
|
46
|
+
*/
|
|
47
|
+
export type HardwareKeyHandle = (
|
|
48
|
+
| { secureEnclave: string }
|
|
49
|
+
| { tpm: string }
|
|
50
|
+
| { tpmPublic: string }
|
|
51
|
+
) & {
|
|
52
|
+
sealed: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type HardwareKey = HardwareKeyHandle & { x: string; y: string };
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether this device may have a security chip `createHardwareKey` can use:
|
|
59
|
+
* every Mac and Windows PC, and a Linux machine whose kernel exposes a TPM.
|
|
60
|
+
*/
|
|
61
|
+
export function hasSecurityChip(): boolean {
|
|
62
|
+
return (
|
|
63
|
+
process.platform === 'darwin' ||
|
|
64
|
+
process.platform === 'win32' ||
|
|
65
|
+
(process.platform === 'linux' && hasLinuxTpm())
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a P-256 DPoP key sealed by this device's Secure Enclave (macOS) or
|
|
71
|
+
* TPM (Windows, Linux). Throws when there's no such chip, or it can't be
|
|
72
|
+
* used.
|
|
73
|
+
*/
|
|
74
|
+
export function createHardwareKey(): HardwareKey {
|
|
75
|
+
if (
|
|
76
|
+
process.platform !== 'darwin' &&
|
|
77
|
+
process.platform !== 'win32' &&
|
|
78
|
+
process.platform !== 'linux'
|
|
79
|
+
) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`There's no supported security chip on ${process.platform}.`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const { privateKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' });
|
|
85
|
+
const { x, y, d } = privateKey.export({ format: 'jwk' });
|
|
86
|
+
if (!x || !y || !d) {
|
|
87
|
+
throw new Error('Failed to export the DPoP key.');
|
|
88
|
+
}
|
|
89
|
+
const secret = Buffer.from(d, 'base64url');
|
|
90
|
+
try {
|
|
91
|
+
if (process.platform === 'darwin') {
|
|
92
|
+
const { wrapped, sealed } = createSecureEnclaveKey(secret);
|
|
93
|
+
return {
|
|
94
|
+
secureEnclave: wrapped.toString('base64url'),
|
|
95
|
+
sealed: sealed.toString('base64url'),
|
|
96
|
+
x,
|
|
97
|
+
y,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (process.platform === 'linux') {
|
|
101
|
+
const sealed = sealWithLinuxTpm(secret);
|
|
102
|
+
return {
|
|
103
|
+
tpmPublic: sealed.public.toString('base64url'),
|
|
104
|
+
sealed: sealed.private.toString('base64url'),
|
|
105
|
+
x,
|
|
106
|
+
y,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const name = `vercel-cli-dpop-${randomUUID()}`;
|
|
110
|
+
const sealed = createTpmKey(name, secret);
|
|
111
|
+
return { tpm: name, sealed: sealed.toString('base64url'), x, y };
|
|
112
|
+
} finally {
|
|
113
|
+
secret.fill(0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Sign `data` with ECDSA P-256 and SHA-256 using a hardware-sealed key,
|
|
119
|
+
* unsealing it with the chip unless it was unsealed in the last
|
|
120
|
+
* `DPOP_KEY_CACHE_TTL_MS`. Returns the signature as `r || s` (IEEE P1363), as
|
|
121
|
+
* JWS uses for ES256.
|
|
122
|
+
*/
|
|
123
|
+
export function signWithHardwareKey(
|
|
124
|
+
key: HardwareKey,
|
|
125
|
+
data: Uint8Array
|
|
126
|
+
): Buffer {
|
|
127
|
+
return sign('sha256', data, {
|
|
128
|
+
key: unsealed(key),
|
|
129
|
+
dsaEncoding: 'ieee-p1363',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Delete a hardware key once nothing uses it, forgetting it if it's
|
|
135
|
+
* unsealed. A Secure Enclave key and a Linux sealed object are only what's
|
|
136
|
+
* in the login, so only a Windows TPM key is deleted from the chip.
|
|
137
|
+
*/
|
|
138
|
+
export function deleteHardwareKey(key: HardwareKeyHandle): void {
|
|
139
|
+
forget(cacheId(key));
|
|
140
|
+
if ('tpm' in key) {
|
|
141
|
+
deleteTpmKey(key.tpm);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Keys this process unsealed, by `cacheId`.
|
|
146
|
+
const unsealedKeys = new Map<
|
|
147
|
+
string,
|
|
148
|
+
{ key: KeyObject; timer: ReturnType<typeof setTimeout> }
|
|
149
|
+
>();
|
|
150
|
+
|
|
151
|
+
function cacheId(key: HardwareKeyHandle): string {
|
|
152
|
+
if ('secureEnclave' in key) {
|
|
153
|
+
return `secureEnclave:${key.secureEnclave}:${key.sealed}`;
|
|
154
|
+
}
|
|
155
|
+
if ('tpmPublic' in key) {
|
|
156
|
+
return `tpmPublic:${key.tpmPublic}:${key.sealed}`;
|
|
157
|
+
}
|
|
158
|
+
return `tpm:${key.tpm}:${key.sealed}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function unsealed(key: HardwareKey): KeyObject {
|
|
162
|
+
const id = cacheId(key);
|
|
163
|
+
const cached = unsealedKeys.get(id);
|
|
164
|
+
if (cached) {
|
|
165
|
+
return cached.key;
|
|
166
|
+
}
|
|
167
|
+
const keyObject = unseal(key);
|
|
168
|
+
// Dropped when its time is up, even if the process is idle, so nothing
|
|
169
|
+
// holds it longer. The timer doesn't keep the process running.
|
|
170
|
+
const timer = setTimeout(() => forget(id), DPOP_KEY_CACHE_TTL_MS);
|
|
171
|
+
timer.unref?.();
|
|
172
|
+
unsealedKeys.set(id, { key: keyObject, timer });
|
|
173
|
+
return keyObject;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function forget(id: string): void {
|
|
177
|
+
const cached = unsealedKeys.get(id);
|
|
178
|
+
if (cached) {
|
|
179
|
+
clearTimeout(cached.timer);
|
|
180
|
+
unsealedKeys.delete(id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function unseal(key: HardwareKey): KeyObject {
|
|
185
|
+
const sealed = Buffer.from(key.sealed, 'base64url');
|
|
186
|
+
let secret: Buffer;
|
|
187
|
+
if ('secureEnclave' in key) {
|
|
188
|
+
secret = unsealWithSecureEnclaveKey(
|
|
189
|
+
Buffer.from(key.secureEnclave, 'base64url'),
|
|
190
|
+
sealed
|
|
191
|
+
);
|
|
192
|
+
} else if ('tpmPublic' in key) {
|
|
193
|
+
secret = unsealWithLinuxTpm({
|
|
194
|
+
public: Buffer.from(key.tpmPublic, 'base64url'),
|
|
195
|
+
private: sealed,
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
secret = unsealWithTpmKey(key.tpm, sealed);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
// Not every Node version checks that `d` is the private half of `x` and
|
|
202
|
+
// `y` on import, so derive its public key and compare.
|
|
203
|
+
const ecdh = createECDH('prime256v1');
|
|
204
|
+
ecdh.setPrivateKey(
|
|
205
|
+
new Uint8Array(secret.buffer, secret.byteOffset, secret.length)
|
|
206
|
+
);
|
|
207
|
+
const point = ecdh.getPublicKey();
|
|
208
|
+
if (
|
|
209
|
+
point.subarray(1, 33).toString('base64url') !== key.x ||
|
|
210
|
+
point.subarray(33).toString('base64url') !== key.y
|
|
211
|
+
) {
|
|
212
|
+
throw new Error("The unsealed DPoP key doesn't match its public key.");
|
|
213
|
+
}
|
|
214
|
+
return createPrivateKey({
|
|
215
|
+
key: {
|
|
216
|
+
kty: 'EC',
|
|
217
|
+
crv: 'P-256',
|
|
218
|
+
x: key.x,
|
|
219
|
+
y: key.y,
|
|
220
|
+
d: secret.toString('base64url'),
|
|
221
|
+
},
|
|
222
|
+
format: 'jwk',
|
|
223
|
+
});
|
|
224
|
+
} finally {
|
|
225
|
+
secret.fill(0);
|
|
226
|
+
}
|
|
227
|
+
}
|
package/koffi.d.ts
ADDED
package/koffi.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadKoffi = loadKoffi;
|
|
4
|
+
// koffi is an optional dependency, so load it only when the chip is used.
|
|
5
|
+
function loadKoffi() {
|
|
6
|
+
return require('koffi');
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=koffi.js.map
|
package/koffi.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"koffi.js","sourceRoot":"","sources":["koffi.ts"],"names":[],"mappings":";;AAGA,8BAEC;AAHD,0EAA0E;AAC1E,SAAgB,SAAS;IACvB,OAAO,OAAO,CAAC,OAAO,CAAU,CAAC;AACnC,CAAC"}
|
package/koffi.ts
ADDED
package/linux-tpm.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const TPM_DEVICE = "/dev/tpmrm0";
|
|
2
|
+
/** A connection to a TPM: sends a command and returns its response. */
|
|
3
|
+
export interface TpmConnection {
|
|
4
|
+
transmit(command: Buffer): Buffer;
|
|
5
|
+
close(): void;
|
|
6
|
+
}
|
|
7
|
+
export type ConnectTpm = () => TpmConnection;
|
|
8
|
+
export declare class LinuxTpmError extends Error {
|
|
9
|
+
readonly rc: number;
|
|
10
|
+
constructor(what: string, rc: number, reason?: string);
|
|
11
|
+
}
|
|
12
|
+
/** This user isn't allowed to open the TPM device. */
|
|
13
|
+
export declare class TpmAccessError extends Error {
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
/** Whether this machine has a TPM the kernel exposes to user space. */
|
|
17
|
+
export declare function hasLinuxTpm(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Seal `secret` with this machine's TPM. Returns the sealed object's public
|
|
20
|
+
* area and its private area, encrypted by the TPM, both as TPM2B structures.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sealWithLinuxTpm(secret: Buffer, connect?: ConnectTpm): {
|
|
23
|
+
public: Buffer;
|
|
24
|
+
private: Buffer;
|
|
25
|
+
};
|
|
26
|
+
/** Unseal what `sealWithLinuxTpm` sealed, with this machine's TPM. */
|
|
27
|
+
export declare function unsealWithLinuxTpm(sealed: {
|
|
28
|
+
public: Buffer;
|
|
29
|
+
private: Buffer;
|
|
30
|
+
}, connect?: ConnectTpm): Buffer;
|
package/linux-tpm.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Seals a secret with this Linux machine's TPM 2.0, by sending TPM commands
|
|
3
|
+
// to the kernel's resource manager (`/dev/tpmrm0`) directly, so there's no
|
|
4
|
+
// native code or library (tpm2-tss) to install.
|
|
5
|
+
//
|
|
6
|
+
// The secret goes into a sealed data object under the owner hierarchy's
|
|
7
|
+
// storage key. The TPM derives that storage key from a seed that never
|
|
8
|
+
// leaves it, the same way every time, so nothing is kept in the TPM: what we
|
|
9
|
+
// save is the object's public area and its private area, which the TPM
|
|
10
|
+
// encrypted with that storage key and which only this TPM can load.
|
|
11
|
+
//
|
|
12
|
+
// The commands use password sessions with empty passwords, without
|
|
13
|
+
// parameter encryption, so the secret crosses the bus between the CPU and a
|
|
14
|
+
// discrete TPM in the clear when it's sealed and unsealed. That's within
|
|
15
|
+
// what sealing protects against (a copied login), not a physical attacker.
|
|
16
|
+
//
|
|
17
|
+
// Buffers that held the secret are zeroed once they're used. That's best
|
|
18
|
+
// effort: JavaScript can keep other copies, and the caller turns the secret
|
|
19
|
+
// into a key.
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.TpmAccessError = exports.LinuxTpmError = exports.TPM_DEVICE = void 0;
|
|
22
|
+
exports.hasLinuxTpm = hasLinuxTpm;
|
|
23
|
+
exports.sealWithLinuxTpm = sealWithLinuxTpm;
|
|
24
|
+
exports.unsealWithLinuxTpm = unsealWithLinuxTpm;
|
|
25
|
+
const node_fs_1 = require("node:fs");
|
|
26
|
+
exports.TPM_DEVICE = '/dev/tpmrm0';
|
|
27
|
+
// Constants from the TPM 2.0 Library spec, Part 2.
|
|
28
|
+
const TPM_ST_NO_SESSIONS = 0x8001;
|
|
29
|
+
const TPM_ST_SESSIONS = 0x8002;
|
|
30
|
+
const TPM_CC_CREATE_PRIMARY = 0x131;
|
|
31
|
+
const TPM_CC_CREATE = 0x153;
|
|
32
|
+
const TPM_CC_LOAD = 0x157;
|
|
33
|
+
const TPM_CC_UNSEAL = 0x15e;
|
|
34
|
+
const TPM_CC_FLUSH_CONTEXT = 0x165;
|
|
35
|
+
const TPM_RH_OWNER = 0x40000001;
|
|
36
|
+
const TPM_RS_PW = 0x40000009;
|
|
37
|
+
const TPM_ALG_AES = 0x0006;
|
|
38
|
+
const TPM_ALG_KEYEDHASH = 0x0008;
|
|
39
|
+
const TPM_ALG_SHA256 = 0x000b;
|
|
40
|
+
const TPM_ALG_NULL = 0x0010;
|
|
41
|
+
const TPM_ALG_ECC = 0x0023;
|
|
42
|
+
const TPM_ALG_CFB = 0x0043;
|
|
43
|
+
const TPM_ECC_NIST_P256 = 0x0003;
|
|
44
|
+
const FIXED_TPM = 1 << 1;
|
|
45
|
+
const FIXED_PARENT = 1 << 4;
|
|
46
|
+
const SENSITIVE_DATA_ORIGIN = 1 << 5;
|
|
47
|
+
const USER_WITH_AUTH = 1 << 6;
|
|
48
|
+
const NO_DA = 1 << 10;
|
|
49
|
+
const RESTRICTED = 1 << 16;
|
|
50
|
+
const DECRYPT = 1 << 17;
|
|
51
|
+
// The TPM is busy: send the command again.
|
|
52
|
+
const TPM_RC_RETRY = 0x922;
|
|
53
|
+
const TPM_RC_YIELDED = 0x908;
|
|
54
|
+
const TPM_RC_TESTING = 0x90a;
|
|
55
|
+
const MAX_RETRIES = 10;
|
|
56
|
+
// How long to wait before sending a command again, times the attempt.
|
|
57
|
+
const RETRY_DELAY_MS = 50;
|
|
58
|
+
// Format-one response codes (bit 7 set) carry an error number in their low
|
|
59
|
+
// six bits: TPM_RC_BAD_AUTH and TPM_RC_AUTH_FAIL mean a wrong password.
|
|
60
|
+
const RC_FMT1 = 0x080;
|
|
61
|
+
const RC_BAD_AUTH = 0x022;
|
|
62
|
+
const RC_AUTH_FAIL = 0x00e;
|
|
63
|
+
const MAX_RESPONSE_SIZE = 4096;
|
|
64
|
+
// The storage key's template: the TCG's standard ECC P-256 SRK template, so
|
|
65
|
+
// the TPM derives the same key from the owner seed every time.
|
|
66
|
+
const STORAGE_KEY_TEMPLATE = tpm2b(concat([
|
|
67
|
+
u16(TPM_ALG_ECC),
|
|
68
|
+
u16(TPM_ALG_SHA256),
|
|
69
|
+
u32(FIXED_TPM |
|
|
70
|
+
FIXED_PARENT |
|
|
71
|
+
SENSITIVE_DATA_ORIGIN |
|
|
72
|
+
USER_WITH_AUTH |
|
|
73
|
+
NO_DA |
|
|
74
|
+
RESTRICTED |
|
|
75
|
+
DECRYPT),
|
|
76
|
+
tpm2b(Buffer.alloc(0)), // authPolicy
|
|
77
|
+
u16(TPM_ALG_AES),
|
|
78
|
+
u16(128),
|
|
79
|
+
u16(TPM_ALG_CFB),
|
|
80
|
+
u16(TPM_ALG_NULL), // scheme
|
|
81
|
+
u16(TPM_ECC_NIST_P256),
|
|
82
|
+
u16(TPM_ALG_NULL), // kdf
|
|
83
|
+
tpm2b(Buffer.alloc(0)), // unique.x
|
|
84
|
+
tpm2b(Buffer.alloc(0)), // unique.y
|
|
85
|
+
]));
|
|
86
|
+
// The sealed object's template: data only this TPM can load, and only under
|
|
87
|
+
// this storage key, unsealed with an empty password.
|
|
88
|
+
const SEALED_OBJECT_TEMPLATE = tpm2b(concat([
|
|
89
|
+
u16(TPM_ALG_KEYEDHASH),
|
|
90
|
+
u16(TPM_ALG_SHA256),
|
|
91
|
+
u32(FIXED_TPM | FIXED_PARENT | USER_WITH_AUTH | NO_DA),
|
|
92
|
+
tpm2b(Buffer.alloc(0)), // authPolicy
|
|
93
|
+
u16(TPM_ALG_NULL), // scheme
|
|
94
|
+
tpm2b(Buffer.alloc(0)), // unique
|
|
95
|
+
]));
|
|
96
|
+
class LinuxTpmError extends Error {
|
|
97
|
+
constructor(what, rc, reason = '') {
|
|
98
|
+
super(`${reason}${what} failed with 0x${rc.toString(16)}.`);
|
|
99
|
+
this.rc = rc;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.LinuxTpmError = LinuxTpmError;
|
|
103
|
+
/** This user isn't allowed to open the TPM device. */
|
|
104
|
+
class TpmAccessError extends Error {
|
|
105
|
+
constructor() {
|
|
106
|
+
super(`Your user can't open ${exports.TPM_DEVICE}. The group that owns it (usually tss) can, but joining it gives your user access to the rest of the TPM too, so only join it if that's fine on this machine.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.TpmAccessError = TpmAccessError;
|
|
110
|
+
/** Whether this machine has a TPM the kernel exposes to user space. */
|
|
111
|
+
function hasLinuxTpm() {
|
|
112
|
+
return (0, node_fs_1.existsSync)(exports.TPM_DEVICE);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Seal `secret` with this machine's TPM. Returns the sealed object's public
|
|
116
|
+
* area and its private area, encrypted by the TPM, both as TPM2B structures.
|
|
117
|
+
*/
|
|
118
|
+
function sealWithLinuxTpm(secret, connect = connectToDevice) {
|
|
119
|
+
return withStorageKey(connect, (tpm, parent) => {
|
|
120
|
+
const data = tpm2b(secret);
|
|
121
|
+
const body = concat([tpm2b(Buffer.alloc(0)), data]);
|
|
122
|
+
const sensitive = tpm2b(body);
|
|
123
|
+
const cmd = command(TPM_CC_CREATE, parent, [
|
|
124
|
+
sensitive,
|
|
125
|
+
SEALED_OBJECT_TEMPLATE,
|
|
126
|
+
tpm2b(Buffer.alloc(0)), // outsideInfo
|
|
127
|
+
u32(0), // creationPCR
|
|
128
|
+
]);
|
|
129
|
+
for (const copy of [data, body, sensitive]) {
|
|
130
|
+
copy.fill(0);
|
|
131
|
+
}
|
|
132
|
+
let response;
|
|
133
|
+
try {
|
|
134
|
+
response = send(tpm, 'TPM2_Create', cmd);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
cmd.fill(0);
|
|
138
|
+
}
|
|
139
|
+
// Header, then parameterSize, then outPrivate and outPublic.
|
|
140
|
+
const sealedPrivate = readTpm2b(response, 14);
|
|
141
|
+
const sealedPublic = readTpm2b(response, 14 + sealedPrivate.length);
|
|
142
|
+
return { public: sealedPublic, private: sealedPrivate };
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/** Unseal what `sealWithLinuxTpm` sealed, with this machine's TPM. */
|
|
146
|
+
function unsealWithLinuxTpm(sealed, connect = connectToDevice) {
|
|
147
|
+
return withStorageKey(connect, (tpm, parent) => {
|
|
148
|
+
const object = send(tpm, 'TPM2_Load', command(TPM_CC_LOAD, parent, [sealed.private, sealed.public])).readUInt32BE(10);
|
|
149
|
+
try {
|
|
150
|
+
const response = send(tpm, 'TPM2_Unseal', command(TPM_CC_UNSEAL, object, []));
|
|
151
|
+
const outData = readTpm2b(response, 14);
|
|
152
|
+
const secret = Buffer.from(new Uint8Array(outData.subarray(2)));
|
|
153
|
+
outData.fill(0);
|
|
154
|
+
response.fill(0);
|
|
155
|
+
return secret;
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
flush(tpm, object);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function withStorageKey(connect, fn) {
|
|
163
|
+
const tpm = connect();
|
|
164
|
+
try {
|
|
165
|
+
let parent;
|
|
166
|
+
try {
|
|
167
|
+
parent = send(tpm, 'TPM2_CreatePrimary', command(TPM_CC_CREATE_PRIMARY, TPM_RH_OWNER, [
|
|
168
|
+
// inSensitive: no password, no data.
|
|
169
|
+
tpm2b(concat([tpm2b(Buffer.alloc(0)), tpm2b(Buffer.alloc(0))])),
|
|
170
|
+
STORAGE_KEY_TEMPLATE,
|
|
171
|
+
tpm2b(Buffer.alloc(0)), // outsideInfo
|
|
172
|
+
u32(0), // creationPCR
|
|
173
|
+
])).readUInt32BE(10);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error instanceof LinuxTpmError && isWrongPassword(error.rc)) {
|
|
177
|
+
throw new LinuxTpmError('TPM2_CreatePrimary', error.rc, "This TPM's owner hierarchy has a password, which the CLI doesn't use: ");
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return fn(tpm, parent);
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
flush(tpm, parent);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
// The resource manager also flushes what this connection loaded.
|
|
190
|
+
tpm.close();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function isWrongPassword(rc) {
|
|
194
|
+
const error = rc & 0x3f;
|
|
195
|
+
return ((rc & RC_FMT1) !== 0 && (error === RC_BAD_AUTH || error === RC_AUTH_FAIL));
|
|
196
|
+
}
|
|
197
|
+
function flush(tpm, handle) {
|
|
198
|
+
try {
|
|
199
|
+
const body = u32(handle);
|
|
200
|
+
send(tpm, 'TPM2_FlushContext', concat([
|
|
201
|
+
u16(TPM_ST_NO_SESSIONS),
|
|
202
|
+
u32(10 + body.length),
|
|
203
|
+
u32(TPM_CC_FLUSH_CONTEXT),
|
|
204
|
+
body,
|
|
205
|
+
]));
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Closing the connection flushes it anyway.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// A command with one handle, authorized by an empty password.
|
|
212
|
+
function command(code, handle, parameters) {
|
|
213
|
+
const auth = concat([
|
|
214
|
+
u32(TPM_RS_PW),
|
|
215
|
+
tpm2b(Buffer.alloc(0)), // nonce
|
|
216
|
+
Buffer.from([0]), // sessionAttributes
|
|
217
|
+
tpm2b(Buffer.alloc(0)), // password
|
|
218
|
+
]);
|
|
219
|
+
const body = concat([u32(handle), u32(auth.length), auth, ...parameters]);
|
|
220
|
+
const cmd = concat([
|
|
221
|
+
u16(TPM_ST_SESSIONS),
|
|
222
|
+
u32(10 + body.length),
|
|
223
|
+
u32(code),
|
|
224
|
+
body,
|
|
225
|
+
]);
|
|
226
|
+
// It may hold a secret.
|
|
227
|
+
body.fill(0);
|
|
228
|
+
return cmd;
|
|
229
|
+
}
|
|
230
|
+
function send(tpm, what, cmd) {
|
|
231
|
+
for (let attempt = 0;; attempt++) {
|
|
232
|
+
const response = tpm.transmit(cmd);
|
|
233
|
+
if (response.length < 10 || response.readUInt32BE(2) !== response.length) {
|
|
234
|
+
throw new Error(`${what} returned a malformed response.`);
|
|
235
|
+
}
|
|
236
|
+
const rc = response.readUInt32BE(6);
|
|
237
|
+
if (rc === 0) {
|
|
238
|
+
return response;
|
|
239
|
+
}
|
|
240
|
+
const busy = rc === TPM_RC_RETRY || rc === TPM_RC_YIELDED || rc === TPM_RC_TESTING;
|
|
241
|
+
if (!busy || attempt >= MAX_RETRIES) {
|
|
242
|
+
throw new LinuxTpmError(what, rc);
|
|
243
|
+
}
|
|
244
|
+
sleep(RETRY_DELAY_MS * (attempt + 1));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Wait without returning to the event loop: every TPM call is synchronous.
|
|
248
|
+
function sleep(ms) {
|
|
249
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
250
|
+
}
|
|
251
|
+
function connectToDevice() {
|
|
252
|
+
let fd;
|
|
253
|
+
try {
|
|
254
|
+
fd = (0, node_fs_1.openSync)(exports.TPM_DEVICE, 'r+');
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const code = error.code;
|
|
258
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
259
|
+
throw new TpmAccessError();
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
transmit(cmd) {
|
|
265
|
+
(0, node_fs_1.writeSync)(fd, view(cmd));
|
|
266
|
+
// The kernel returns the whole response in one read.
|
|
267
|
+
const response = Buffer.alloc(MAX_RESPONSE_SIZE);
|
|
268
|
+
const size = (0, node_fs_1.readSync)(fd, view(response), 0, response.length, null);
|
|
269
|
+
const result = Buffer.from(new Uint8Array(response.subarray(0, size)));
|
|
270
|
+
response.fill(0);
|
|
271
|
+
return result;
|
|
272
|
+
},
|
|
273
|
+
close() {
|
|
274
|
+
(0, node_fs_1.closeSync)(fd);
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// A TPM2B structure (a 16-bit size, then that many bytes) at `offset`,
|
|
279
|
+
// including its size.
|
|
280
|
+
function readTpm2b(buffer, offset) {
|
|
281
|
+
const end = offset + 2 + buffer.readUInt16BE(offset);
|
|
282
|
+
if (end > buffer.length) {
|
|
283
|
+
throw new Error('The TPM returned a truncated response.');
|
|
284
|
+
}
|
|
285
|
+
return Buffer.from(new Uint8Array(buffer.subarray(offset, end)));
|
|
286
|
+
}
|
|
287
|
+
function tpm2b(bytes) {
|
|
288
|
+
return concat([u16(bytes.length), bytes]);
|
|
289
|
+
}
|
|
290
|
+
function concat(parts) {
|
|
291
|
+
const result = Buffer.alloc(parts.reduce((size, p) => size + p.length, 0));
|
|
292
|
+
let offset = 0;
|
|
293
|
+
for (const part of parts) {
|
|
294
|
+
result.set(part, offset);
|
|
295
|
+
offset += part.length;
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
// A view `fs` accepts, whichever `@types/node` is installed.
|
|
300
|
+
function view(buffer) {
|
|
301
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.length);
|
|
302
|
+
}
|
|
303
|
+
function u16(value) {
|
|
304
|
+
const bytes = Buffer.alloc(2);
|
|
305
|
+
bytes.writeUInt16BE(value);
|
|
306
|
+
return bytes;
|
|
307
|
+
}
|
|
308
|
+
function u32(value) {
|
|
309
|
+
const bytes = Buffer.alloc(4);
|
|
310
|
+
bytes.writeUInt32BE(value >>> 0);
|
|
311
|
+
return bytes;
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=linux-tpm.js.map
|
package/linux-tpm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linux-tpm.js","sourceRoot":"","sources":["linux-tpm.ts"],"names":[],"mappings":"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"}
|