ds-01 1.0.14 → 1.1.1
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/.turbo/turbo-build.log +3 -2
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +16 -61
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -45
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +20 -60
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +0 -2
- package/dist/utils/secureKeyStore.d.ts +0 -5
- package/dist/utils/secureKeyStore.d.ts.map +1 -1
- package/dist/utils/secureKeyStore.js +13 -28
- package/package.json +1 -1
- package/src/commands/add.ts +66 -210
- package/src/commands/init.ts +26 -51
- package/src/commands/login.ts +71 -244
- package/src/commands/logout.ts +0 -2
- package/src/utils/secureKeyStore.ts +41 -88
- package/src/test-key.ts +0 -20
- package/src/utils/deviceKey.ts +0 -19
package/src/commands/login.ts
CHANGED
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
intro,
|
|
4
|
-
outro,
|
|
5
|
-
spinner,
|
|
6
|
-
note,
|
|
7
|
-
cancel,
|
|
8
|
-
} from "@clack/prompts";
|
|
2
|
+
import { intro, outro, spinner, note, cancel } from "@clack/prompts";
|
|
9
3
|
import pc from "picocolors";
|
|
10
4
|
import open from "open";
|
|
11
5
|
import machineIdPkg from "node-machine-id";
|
|
12
|
-
|
|
13
6
|
import { saveToken } from "../utils/config.js";
|
|
14
7
|
import {
|
|
15
8
|
createDeviceKey,
|
|
16
9
|
getDevicePublicKey,
|
|
17
10
|
} from "../utils/secureKeyStore.js";
|
|
18
11
|
|
|
19
|
-
const API_BASE_URL =
|
|
20
|
-
process.env.DS01_API_URL ||
|
|
21
|
-
"https://ds-01.vercel.app";
|
|
12
|
+
const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
|
|
22
13
|
|
|
23
14
|
const { machineIdSync } = machineIdPkg;
|
|
24
15
|
|
|
25
16
|
export const login = new Command()
|
|
26
17
|
.name("login")
|
|
27
|
-
.description(
|
|
28
|
-
"Authenticate your terminal with DS01 via browser",
|
|
29
|
-
)
|
|
18
|
+
.description("Authenticate your terminal with DS01 via browser")
|
|
30
19
|
.action(async () => {
|
|
31
20
|
console.log();
|
|
32
21
|
|
|
33
22
|
intro(
|
|
34
|
-
`${pc.bgWhite(
|
|
35
|
-
pc.black(
|
|
36
|
-
pc.bold(" DS01 "),
|
|
37
|
-
),
|
|
38
|
-
)} ${pc.gray(
|
|
23
|
+
`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray(
|
|
39
24
|
"v1.0.0 — Terminal Authentication",
|
|
40
25
|
)}`,
|
|
41
26
|
);
|
|
@@ -43,300 +28,142 @@ export const login = new Command()
|
|
|
43
28
|
const s = spinner();
|
|
44
29
|
|
|
45
30
|
try {
|
|
46
|
-
|
|
47
|
-
// 1. CREATE / LOAD SECURE DEVICE KEY
|
|
48
|
-
// ==================================================
|
|
49
|
-
|
|
50
|
-
s.start(
|
|
51
|
-
"Preparing secure device identity...",
|
|
52
|
-
);
|
|
31
|
+
s.start("Preparing secure device identity...");
|
|
53
32
|
|
|
54
33
|
await createDeviceKey();
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
await getDevicePublicKey();
|
|
58
|
-
|
|
59
|
-
const hardwareId =
|
|
60
|
-
machineIdSync();
|
|
61
|
-
|
|
34
|
+
const publicKey = await getDevicePublicKey();
|
|
35
|
+
const hardwareId = machineIdSync();
|
|
62
36
|
const publicKeyType =
|
|
63
|
-
process.platform === "win32"
|
|
64
|
-
? "windows-rsa"
|
|
65
|
-
: "macos-ec";
|
|
66
|
-
|
|
67
|
-
// ==================================================
|
|
68
|
-
// 2. REQUEST DEVICE CODE
|
|
69
|
-
// ==================================================
|
|
70
|
-
|
|
71
|
-
s.start(
|
|
72
|
-
"Requesting pairing code...",
|
|
73
|
-
);
|
|
37
|
+
process.platform === "win32" ? "windows-rsa" : "macos-ec";
|
|
74
38
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`${API_BASE_URL}/api/auth/device/code`,
|
|
78
|
-
{
|
|
79
|
-
method: "POST",
|
|
39
|
+
// Req to my server with machineId and certificate for now but eventually will send the publicKey
|
|
40
|
+
s.start("Requesting pairing code...");
|
|
80
41
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
42
|
+
const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
},
|
|
85
47
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
publicKeyType,
|
|
93
|
-
}),
|
|
94
|
-
},
|
|
95
|
-
);
|
|
48
|
+
body: JSON.stringify({
|
|
49
|
+
machineId: hardwareId,
|
|
50
|
+
publicKey,
|
|
51
|
+
publicKeyType,
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
96
54
|
|
|
97
55
|
if (!initResponse.ok) {
|
|
98
|
-
s.stop(
|
|
99
|
-
|
|
100
|
-
"Failed to reach DS01 authorization server.",
|
|
101
|
-
),
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
cancel(
|
|
105
|
-
`Server responded with HTTP ${initResponse.status}`,
|
|
106
|
-
);
|
|
107
|
-
|
|
56
|
+
s.stop(pc.red("Failed to reach DS01 authorization server."));
|
|
57
|
+
cancel(`Server responded with HTTP ${initResponse.status}`);
|
|
108
58
|
process.exit(1);
|
|
109
59
|
}
|
|
110
60
|
|
|
111
|
-
const {
|
|
112
|
-
deviceCode,
|
|
113
|
-
userCode,
|
|
114
|
-
verificationUrl,
|
|
115
|
-
interval,
|
|
116
|
-
} =
|
|
61
|
+
const { deviceCode, userCode, verificationUrl, interval } =
|
|
117
62
|
await initResponse.json();
|
|
118
63
|
|
|
119
|
-
if (
|
|
120
|
-
!deviceCode ||
|
|
121
|
-
!userCode ||
|
|
122
|
-
!verificationUrl
|
|
123
|
-
) {
|
|
64
|
+
if (!deviceCode || !userCode || !verificationUrl) {
|
|
124
65
|
throw new Error(
|
|
125
66
|
"Authorization server returned an invalid device response.",
|
|
126
67
|
);
|
|
127
68
|
}
|
|
128
69
|
|
|
129
|
-
s.stop(
|
|
130
|
-
pc.green(
|
|
131
|
-
"Secure device identity registered.",
|
|
132
|
-
),
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// ==================================================
|
|
136
|
-
// 3. SHOW BROWSER AUTHORIZATION
|
|
137
|
-
// ==================================================
|
|
70
|
+
s.stop(pc.green("Secure device identity registered."));
|
|
138
71
|
|
|
72
|
+
// Opening users browser with verification URL
|
|
139
73
|
note(
|
|
140
|
-
`${pc.bold(
|
|
141
|
-
|
|
142
|
-
)} ${pc.cyan(
|
|
143
|
-
pc.bold(
|
|
144
|
-
` ${userCode} `,
|
|
145
|
-
),
|
|
74
|
+
`${pc.bold("Pairing Code:")} ${pc.cyan(
|
|
75
|
+
pc.bold(` ${userCode} `),
|
|
146
76
|
)}\n` +
|
|
147
|
-
`${pc.bold(
|
|
148
|
-
"Verification URL:",
|
|
149
|
-
)} ${pc.underline(
|
|
150
|
-
verificationUrl,
|
|
151
|
-
)}`,
|
|
77
|
+
`${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`,
|
|
152
78
|
"Action Required",
|
|
153
79
|
);
|
|
154
80
|
|
|
155
81
|
try {
|
|
156
|
-
await open(
|
|
157
|
-
verificationUrl,
|
|
158
|
-
);
|
|
82
|
+
await open(verificationUrl);
|
|
159
83
|
} catch {
|
|
160
84
|
// Browser launch failure is non-fatal.
|
|
161
85
|
}
|
|
86
|
+
|
|
87
|
+
// we polling to one of my server endpoint to check wether the temporary req is approved or not ??
|
|
88
|
+
s.start("Waiting for web authorization...");
|
|
162
89
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// ==================================================
|
|
166
|
-
|
|
167
|
-
s.start(
|
|
168
|
-
"Waiting for web authorization...",
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
const pollInterval =
|
|
172
|
-
(interval || 5) * 1000;
|
|
173
|
-
|
|
174
|
-
let token: string | null =
|
|
175
|
-
null;
|
|
90
|
+
const pollInterval = (interval || 5) * 1000;
|
|
91
|
+
let token: string | null = null;
|
|
176
92
|
|
|
177
93
|
while (true) {
|
|
178
|
-
await new Promise(
|
|
179
|
-
(resolve) =>
|
|
180
|
-
setTimeout(
|
|
181
|
-
resolve,
|
|
182
|
-
pollInterval,
|
|
183
|
-
),
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
const tokenResponse =
|
|
187
|
-
await fetch(
|
|
188
|
-
`${API_BASE_URL}/api/auth/device/token`,
|
|
189
|
-
{
|
|
190
|
-
method: "POST",
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
191
95
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
deviceCode,
|
|
199
|
-
machineId:
|
|
200
|
-
hardwareId,
|
|
201
|
-
}),
|
|
96
|
+
const tokenResponse = await fetch(
|
|
97
|
+
`${API_BASE_URL}/api/auth/device/token`,
|
|
98
|
+
{
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/json",
|
|
202
102
|
},
|
|
203
|
-
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
deviceCode,
|
|
105
|
+
machineId: hardwareId,
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
);
|
|
204
109
|
|
|
205
110
|
let tokenData: any;
|
|
206
|
-
|
|
207
111
|
try {
|
|
208
|
-
tokenData =
|
|
209
|
-
await tokenResponse.json();
|
|
112
|
+
tokenData = await tokenResponse.json();
|
|
210
113
|
} catch {
|
|
211
|
-
s.stop(
|
|
212
|
-
|
|
213
|
-
"Authorization server returned invalid JSON.",
|
|
214
|
-
),
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
cancel(
|
|
218
|
-
"Unable to read authorization response.",
|
|
219
|
-
);
|
|
220
|
-
|
|
114
|
+
s.stop(pc.red("Authorization server returned invalid JSON."));
|
|
115
|
+
cancel("Unable to read authorization response.");
|
|
221
116
|
process.exit(1);
|
|
222
117
|
}
|
|
223
118
|
|
|
224
|
-
//
|
|
225
|
-
// AUTHORIZATION SUCCESS
|
|
226
|
-
// ==================================================
|
|
227
|
-
|
|
119
|
+
// Auth success stop polling
|
|
228
120
|
if (
|
|
229
121
|
tokenResponse.ok &&
|
|
230
|
-
typeof tokenData.accessToken ===
|
|
231
|
-
"string" &&
|
|
122
|
+
typeof tokenData.accessToken === "string" &&
|
|
232
123
|
tokenData.accessToken.length > 0
|
|
233
124
|
) {
|
|
234
|
-
token =
|
|
235
|
-
tokenData.accessToken;
|
|
236
|
-
|
|
125
|
+
token = tokenData.accessToken;
|
|
237
126
|
break;
|
|
238
127
|
}
|
|
239
128
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
// ==================================================
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
tokenData.error ===
|
|
246
|
-
"authorization_pending"
|
|
247
|
-
) {
|
|
129
|
+
// Still waiting keep polling
|
|
130
|
+
if (tokenData.error === "authorization_pending") {
|
|
248
131
|
continue;
|
|
249
132
|
}
|
|
250
133
|
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
s.stop(
|
|
256
|
-
pc.red(
|
|
257
|
-
"Authorization failed or expired.",
|
|
258
|
-
),
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
cancel(
|
|
262
|
-
`Reason: ${
|
|
263
|
-
tokenData.error ||
|
|
264
|
-
"Unknown authorization error"
|
|
265
|
-
}`,
|
|
266
|
-
);
|
|
267
|
-
|
|
134
|
+
// Authorization Failed
|
|
135
|
+
s.stop(pc.red("Authorization failed or expired."));
|
|
136
|
+
cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
|
|
268
137
|
process.exit(1);
|
|
269
138
|
}
|
|
270
139
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
// ==================================================
|
|
274
|
-
|
|
275
|
-
s.stop(
|
|
276
|
-
pc.green(
|
|
277
|
-
"Hardware signature linked & token verified!",
|
|
278
|
-
),
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
// ==================================================
|
|
282
|
-
// 6. ENSURE TOKEN EXISTS
|
|
283
|
-
// ==================================================
|
|
140
|
+
// outside the loop stop the spinner
|
|
141
|
+
s.stop(pc.green("Hardware signature linked & token verified!"));
|
|
284
142
|
|
|
143
|
+
// ENSURE TOKEN EXISTS
|
|
285
144
|
if (!token) {
|
|
286
145
|
throw new Error(
|
|
287
146
|
"Authorization completed without receiving an access token.",
|
|
288
147
|
);
|
|
289
148
|
}
|
|
290
149
|
|
|
291
|
-
|
|
292
|
-
// 7. SAVE TOKEN
|
|
293
|
-
// ==================================================
|
|
294
|
-
|
|
295
|
-
saveToken(
|
|
296
|
-
token,
|
|
297
|
-
hardwareId,
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
// ==================================================
|
|
301
|
-
// 8. SUCCESS
|
|
302
|
-
// ==================================================
|
|
303
|
-
|
|
150
|
+
saveToken(token, hardwareId);
|
|
304
151
|
outro(
|
|
305
|
-
`${pc.white(
|
|
306
|
-
"
|
|
307
|
-
)} ${pc.bold(
|
|
308
|
-
"Terminal synced successfully",
|
|
309
|
-
)}\n` +
|
|
310
|
-
pc.gray(
|
|
311
|
-
"Secure device key and session token are ready.",
|
|
312
|
-
),
|
|
152
|
+
`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
|
|
153
|
+
pc.gray("Secure device key and session token are ready."),
|
|
313
154
|
);
|
|
314
155
|
|
|
315
|
-
//
|
|
316
|
-
// 9. HARD EXIT
|
|
317
|
-
// ==================================================
|
|
318
|
-
//
|
|
156
|
+
// Hard Exit
|
|
319
157
|
// The login command is completely finished.
|
|
320
|
-
|
|
158
|
+
|
|
321
159
|
// This prevents any lingering CLI handles
|
|
322
160
|
// (spinner timers, readline handles, etc.)
|
|
323
161
|
// from keeping the Node process alive.
|
|
324
162
|
//
|
|
325
163
|
process.exit(0);
|
|
326
164
|
} catch (error: any) {
|
|
327
|
-
s.stop(
|
|
328
|
-
|
|
329
|
-
"An error occurred during login.",
|
|
330
|
-
),
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
cancel(
|
|
334
|
-
pc.gray(
|
|
335
|
-
error?.message ||
|
|
336
|
-
"Unknown CLI Error",
|
|
337
|
-
),
|
|
338
|
-
);
|
|
339
|
-
|
|
165
|
+
s.stop(pc.red("An error occurred during login."));
|
|
166
|
+
cancel(pc.gray(error?.message || "Unknown CLI Error"));
|
|
340
167
|
process.exit(1);
|
|
341
168
|
}
|
|
342
|
-
});
|
|
169
|
+
});
|
package/src/commands/logout.ts
CHANGED
|
@@ -41,9 +41,7 @@ export const logout = new Command()
|
|
|
41
41
|
|
|
42
42
|
// NEW: Delete local credentials only after the server confirms
|
|
43
43
|
// that the CLI session has been revoked.
|
|
44
|
-
console.log("✅ Server confirmed CLI session was revoked.");
|
|
45
44
|
deleteToken();
|
|
46
|
-
console.log("✅ Local credentials deleted.");
|
|
47
45
|
spinner.succeed(chalk.green("Successfully logged out."));
|
|
48
46
|
console.log(
|
|
49
47
|
chalk.gray(
|
|
@@ -11,13 +11,16 @@ const execFileAsync = promisify(execFile);
|
|
|
11
11
|
const KEY_NAME = "DS01-CLI-Device-Key";
|
|
12
12
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
// import.meta.ur -- current module ka location URL form mein deta hai
|
|
15
|
+
|
|
14
16
|
const __dirname = path.dirname(__filename);
|
|
17
|
+
//C:\Projects\DS01\cli\dist\utils
|
|
18
|
+
|
|
19
|
+
// __filename = where is THIS FILE?
|
|
20
|
+
// __dirname = which folder contains THIS FILE?
|
|
15
21
|
|
|
16
22
|
function getMacHelperPath(): string {
|
|
17
|
-
return path.resolve(
|
|
18
|
-
__dirname,
|
|
19
|
-
"../native/macos-key-helper.swift",
|
|
20
|
-
);
|
|
23
|
+
return path.resolve(__dirname, "../native/macos-key-helper.swift");
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
function ensureSupportedOS(): void {
|
|
@@ -30,11 +33,7 @@ function ensureSupportedOS(): void {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
* ---------------------------------------------------------
|
|
35
|
-
* WINDOWS
|
|
36
|
-
* ---------------------------------------------------------
|
|
37
|
-
*/
|
|
36
|
+
// for windows
|
|
38
37
|
|
|
39
38
|
async function createWindowsDeviceKey(): Promise<void> {
|
|
40
39
|
const script = `
|
|
@@ -102,21 +101,10 @@ Write-Output "CREATED"
|
|
|
102
101
|
const result = stdout.trim();
|
|
103
102
|
|
|
104
103
|
if (result !== "CREATED" && result !== "EXISTS") {
|
|
105
|
-
throw new Error(
|
|
106
|
-
"Failed to create Windows DS01 device key.",
|
|
107
|
-
);
|
|
104
|
+
throw new Error("Failed to create Windows DS01 device key.");
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
/**
|
|
112
|
-
* Find exactly one valid DS01 certificate.
|
|
113
|
-
*
|
|
114
|
-
* We intentionally do not use Select-Object -First 1.
|
|
115
|
-
*
|
|
116
|
-
* 0 certificates -> error
|
|
117
|
-
* 1 certificate -> use it
|
|
118
|
-
* 2+ certificates -> error
|
|
119
|
-
*/
|
|
120
108
|
function getWindowsCertificateLookup(): string {
|
|
121
109
|
return `
|
|
122
110
|
$certificates = @(Get-ChildItem Cert:\\CurrentUser\\My |
|
|
@@ -138,6 +126,12 @@ $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]$certifi
|
|
|
138
126
|
`;
|
|
139
127
|
}
|
|
140
128
|
|
|
129
|
+
// cert - just a variable holding the certificate object
|
|
130
|
+
// X509Certificate2 is a Windows/.NET certificate object
|
|
131
|
+
// that gives the code a standard way to work with the certificate and its associated key information
|
|
132
|
+
// X509Certificate2 a class/type, rather than simply a "format"
|
|
133
|
+
|
|
134
|
+
|
|
141
135
|
async function getWindowsPublicKey(): Promise<string> {
|
|
142
136
|
const script = `
|
|
143
137
|
$ErrorActionPreference = "Stop"
|
|
@@ -165,19 +159,14 @@ ${getWindowsCertificateLookup()}
|
|
|
165
159
|
const certificate = stdout.trim();
|
|
166
160
|
|
|
167
161
|
if (!certificate) {
|
|
168
|
-
throw new Error(
|
|
169
|
-
"Unable to read Windows DS01 public certificate.",
|
|
170
|
-
);
|
|
162
|
+
throw new Error("Unable to read Windows DS01 public certificate.");
|
|
171
163
|
}
|
|
172
164
|
|
|
173
165
|
return certificate;
|
|
174
166
|
}
|
|
175
167
|
|
|
176
|
-
async function signWindowsDeviceKey(
|
|
177
|
-
data
|
|
178
|
-
): Promise<Uint8Array> {
|
|
179
|
-
const dataBase64 =
|
|
180
|
-
Buffer.from(data).toString("base64");
|
|
168
|
+
async function signWindowsDeviceKey(data: Uint8Array): Promise<Uint8Array> {
|
|
169
|
+
const dataBase64 = Buffer.from(data).toString("base64");
|
|
181
170
|
|
|
182
171
|
const script = `
|
|
183
172
|
$ErrorActionPreference = "Stop"
|
|
@@ -226,17 +215,16 @@ finally {
|
|
|
226
215
|
},
|
|
227
216
|
);
|
|
228
217
|
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
229
221
|
const signature = stdout.trim();
|
|
230
222
|
|
|
231
223
|
if (!signature) {
|
|
232
|
-
throw new Error(
|
|
233
|
-
"Failed to sign data with Windows device key.",
|
|
234
|
-
);
|
|
224
|
+
throw new Error("Failed to sign data with Windows device key.");
|
|
235
225
|
}
|
|
236
226
|
|
|
237
|
-
return new Uint8Array(
|
|
238
|
-
Buffer.from(signature, "base64"),
|
|
239
|
-
);
|
|
227
|
+
return new Uint8Array(Buffer.from(signature, "base64"));
|
|
240
228
|
}
|
|
241
229
|
|
|
242
230
|
async function deleteWindowsDeviceKey(): Promise<void> {
|
|
@@ -277,18 +265,10 @@ Write-Output "DELETED"
|
|
|
277
265
|
);
|
|
278
266
|
}
|
|
279
267
|
|
|
280
|
-
|
|
281
|
-
* ---------------------------------------------------------
|
|
282
|
-
* MACOS
|
|
283
|
-
* ---------------------------------------------------------
|
|
284
|
-
*/
|
|
268
|
+
// for Mac
|
|
285
269
|
|
|
286
270
|
async function runMacHelper(
|
|
287
|
-
operation:
|
|
288
|
-
| "create"
|
|
289
|
-
| "public"
|
|
290
|
-
| "sign"
|
|
291
|
-
| "delete",
|
|
271
|
+
operation: "create" | "public" | "sign" | "delete",
|
|
292
272
|
data?: Uint8Array,
|
|
293
273
|
): Promise<string> {
|
|
294
274
|
const helper = getMacHelperPath();
|
|
@@ -296,76 +276,51 @@ async function runMacHelper(
|
|
|
296
276
|
const args = [helper, operation];
|
|
297
277
|
|
|
298
278
|
if (data) {
|
|
299
|
-
args.push(
|
|
300
|
-
Buffer.from(data).toString("base64"),
|
|
301
|
-
);
|
|
279
|
+
args.push(Buffer.from(data).toString("base64"));
|
|
302
280
|
}
|
|
303
281
|
|
|
304
|
-
const { stdout } = await execFileAsync(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
{
|
|
308
|
-
windowsHide: true,
|
|
309
|
-
},
|
|
310
|
-
);
|
|
282
|
+
const { stdout } = await execFileAsync("swift", args, {
|
|
283
|
+
windowsHide: true,
|
|
284
|
+
});
|
|
311
285
|
|
|
312
286
|
return stdout.trim();
|
|
313
287
|
}
|
|
314
288
|
|
|
315
289
|
async function createMacDeviceKey(): Promise<void> {
|
|
316
|
-
const result =
|
|
317
|
-
await runMacHelper("create");
|
|
290
|
+
const result = await runMacHelper("create");
|
|
318
291
|
|
|
319
|
-
if (
|
|
320
|
-
result !== "CREATED" &&
|
|
321
|
-
result !== "EXISTS"
|
|
322
|
-
) {
|
|
292
|
+
if (result !== "CREATED" && result !== "EXISTS") {
|
|
323
293
|
throw new Error(
|
|
324
|
-
result ||
|
|
325
|
-
"Failed to create macOS Secure Enclave device key.",
|
|
294
|
+
result || "Failed to create macOS Secure Enclave device key.",
|
|
326
295
|
);
|
|
327
296
|
}
|
|
328
297
|
}
|
|
329
298
|
|
|
330
299
|
async function getMacPublicKey(): Promise<string> {
|
|
331
|
-
const result =
|
|
332
|
-
await runMacHelper("public");
|
|
300
|
+
const result = await runMacHelper("public");
|
|
333
301
|
|
|
334
302
|
if (!result) {
|
|
335
|
-
throw new Error(
|
|
336
|
-
"Unable to read macOS DS01 public key.",
|
|
337
|
-
);
|
|
303
|
+
throw new Error("Unable to read macOS DS01 public key.");
|
|
338
304
|
}
|
|
339
305
|
|
|
340
306
|
return result;
|
|
341
307
|
}
|
|
342
308
|
|
|
343
|
-
async function signMacDeviceKey(
|
|
344
|
-
|
|
345
|
-
): Promise<Uint8Array> {
|
|
346
|
-
const result =
|
|
347
|
-
await runMacHelper("sign", data);
|
|
309
|
+
async function signMacDeviceKey(data: Uint8Array): Promise<Uint8Array> {
|
|
310
|
+
const result = await runMacHelper("sign", data);
|
|
348
311
|
|
|
349
312
|
if (!result) {
|
|
350
|
-
throw new Error(
|
|
351
|
-
"Failed to sign data with macOS Secure Enclave key.",
|
|
352
|
-
);
|
|
313
|
+
throw new Error("Failed to sign data with macOS Secure Enclave key.");
|
|
353
314
|
}
|
|
354
315
|
|
|
355
|
-
return new Uint8Array(
|
|
356
|
-
Buffer.from(result, "base64"),
|
|
357
|
-
);
|
|
316
|
+
return new Uint8Array(Buffer.from(result, "base64"));
|
|
358
317
|
}
|
|
359
318
|
|
|
360
319
|
async function deleteMacDeviceKey(): Promise<void> {
|
|
361
320
|
await runMacHelper("delete");
|
|
362
321
|
}
|
|
363
322
|
|
|
364
|
-
|
|
365
|
-
* ---------------------------------------------------------
|
|
366
|
-
* PUBLIC API
|
|
367
|
-
* ---------------------------------------------------------
|
|
368
|
-
*/
|
|
323
|
+
// Public APIs which we use this for creating keys and certificate
|
|
369
324
|
|
|
370
325
|
export async function createDeviceKey(): Promise<void> {
|
|
371
326
|
ensureSupportedOS();
|
|
@@ -388,9 +343,7 @@ export async function getDevicePublicKey(): Promise<string> {
|
|
|
388
343
|
return getMacPublicKey();
|
|
389
344
|
}
|
|
390
345
|
|
|
391
|
-
export async function signWithDeviceKey(
|
|
392
|
-
data: Uint8Array,
|
|
393
|
-
): Promise<Uint8Array> {
|
|
346
|
+
export async function signWithDeviceKey(data: Uint8Array): Promise<Uint8Array> {
|
|
394
347
|
ensureSupportedOS();
|
|
395
348
|
|
|
396
349
|
if (os.platform() === "win32") {
|
|
@@ -409,4 +362,4 @@ export async function deleteDeviceKey(): Promise<void> {
|
|
|
409
362
|
}
|
|
410
363
|
|
|
411
364
|
await deleteMacDeviceKey();
|
|
412
|
-
}
|
|
365
|
+
}
|
package/src/test-key.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createDeviceKey,
|
|
3
|
-
getDevicePublicKey,
|
|
4
|
-
signWithDeviceKey,
|
|
5
|
-
} from "./utils/secureKeyStore.js";
|
|
6
|
-
|
|
7
|
-
await createDeviceKey();
|
|
8
|
-
|
|
9
|
-
const publicKey = await getDevicePublicKey();
|
|
10
|
-
|
|
11
|
-
console.log("Public key:", publicKey);
|
|
12
|
-
|
|
13
|
-
const message = new TextEncoder().encode("hello DS01");
|
|
14
|
-
|
|
15
|
-
const signature = await signWithDeviceKey(message);
|
|
16
|
-
|
|
17
|
-
console.log(
|
|
18
|
-
"Signature:",
|
|
19
|
-
Buffer.from(signature).toString("base64"),
|
|
20
|
-
);
|