ds-01 1.0.13 → 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.
@@ -8,17 +8,20 @@ func fail(_ message: String) -> Never {
8
8
  exit(1)
9
9
  }
10
10
 
11
- // Find the existing Secure Enclave private key.
11
+ // Find our existing private key in the current user's Keychain.
12
12
  func findPrivateKey() -> SecKey? {
13
13
  let query: [String: Any] = [
14
- kSecClass as String: kSecClassKey,
14
+ kSecClass as String:
15
+ kSecClassKey,
16
+
15
17
  kSecAttrApplicationTag as String:
16
18
  keyTag.data(using: .utf8)!,
19
+
17
20
  kSecAttrKeyType as String:
18
21
  kSecAttrKeyTypeECSECPrimeRandom,
19
- kSecAttrTokenID as String:
20
- kSecAttrTokenIDSecureEnclave,
21
- kSecReturnRef as String: true
22
+
23
+ kSecReturnRef as String:
24
+ true
22
25
  ]
23
26
 
24
27
  var result: CFTypeRef?
@@ -35,54 +38,16 @@ func findPrivateKey() -> SecKey? {
35
38
  return (result as! SecKey)
36
39
  }
37
40
 
41
+
42
+ // Create a persistent EC P-256 private key.
38
43
  func createKey() {
39
44
 
40
- // If the key already exists, don't create another one.
45
+ // Don't create a second key if one already exists.
41
46
  if findPrivateKey() != nil {
42
47
  print("EXISTS")
43
48
  return
44
49
  }
45
50
 
46
- // Keychain access control.
47
- //
48
- // WhenUnlockedThisDeviceOnly means:
49
- // - available only while the device is unlocked
50
- // - cannot be restored onto another device
51
- //
52
- // privateKeyUsage means the key can be used for signing.
53
- var accessError: Unmanaged<CFError>?
54
-
55
- guard let access = SecAccessControlCreateWithFlags(
56
- nil,
57
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
58
- .privateKeyUsage,
59
- &accessError
60
- ) else {
61
-
62
- if let error = accessError {
63
- fail(
64
- "Unable to create Keychain access control: " +
65
- error.takeRetainedValue().localizedDescription
66
- )
67
- }
68
-
69
- fail("Unable to create Keychain access control.")
70
- }
71
-
72
- let privateAttributes: [String: Any] = [
73
-
74
- // Permanently store the private key.
75
- kSecAttrIsPermanent as String: true,
76
-
77
- // Identifier used to find the key later.
78
- kSecAttrApplicationTag as String:
79
- keyTag.data(using: .utf8)!,
80
-
81
- // Access rules for the private key.
82
- kSecAttrAccessControl as String:
83
- access
84
- ]
85
-
86
51
  let attributes: [String: Any] = [
87
52
 
88
53
  // EC P-256 key.
@@ -92,14 +57,15 @@ func createKey() {
92
57
  kSecAttrKeySizeInBits as String:
93
58
  256,
94
59
 
95
- // IMPORTANT:
96
- // The private key lives inside the Secure Enclave.
97
- kSecAttrTokenID as String:
98
- kSecAttrTokenIDSecureEnclave,
60
+ // Store the private key permanently in Keychain.
61
+ kSecPrivateKeyAttrs as String: [
62
+
63
+ kSecAttrIsPermanent as String:
64
+ true,
99
65
 
100
- // Private key configuration.
101
- kSecPrivateKeyAttrs as String:
102
- privateAttributes
66
+ kSecAttrApplicationTag as String:
67
+ keyTag.data(using: .utf8)!
68
+ ]
103
69
  ]
104
70
 
105
71
  var error: Unmanaged<CFError>?
@@ -111,28 +77,30 @@ func createKey() {
111
77
 
112
78
  if let error {
113
79
  fail(
114
- "Secure Enclave key creation failed: " +
80
+ "Keychain key creation failed: " +
115
81
  error.takeRetainedValue().localizedDescription
116
82
  )
117
83
  }
118
84
 
119
- fail("Secure Enclave key creation failed.")
85
+ fail("Keychain key creation failed.")
120
86
  }
121
87
 
122
88
  print("CREATED")
123
89
  }
124
90
 
125
91
 
126
- // Return ONLY the public key.
92
+ // Return only the public key.
127
93
  //
128
94
  // The private key is never exported.
129
95
  func publicKey() {
130
96
 
131
97
  guard let privateKey = findPrivateKey() else {
132
- fail("DS01 Secure Enclave key not found.")
98
+ fail("DS01 device key not found.")
133
99
  }
134
100
 
135
- guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
101
+ guard let publicKey = SecKeyCopyPublicKey(
102
+ privateKey
103
+ ) else {
136
104
  fail("Unable to obtain public key.")
137
105
  }
138
106
 
@@ -159,11 +127,11 @@ func publicKey() {
159
127
  }
160
128
 
161
129
 
162
- // Sign data using the Secure Enclave private key.
130
+ // Sign data using the Keychain private key.
163
131
  func sign(_ base64Data: String) {
164
132
 
165
133
  guard let privateKey = findPrivateKey() else {
166
- fail("DS01 Secure Enclave key not found.")
134
+ fail("DS01 device key not found.")
167
135
  }
168
136
 
169
137
  guard let data = Data(
@@ -180,9 +148,7 @@ func sign(_ base64Data: String) {
180
148
  .sign,
181
149
  algorithm
182
150
  ) else {
183
- fail(
184
- "Secure Enclave key does not support signing."
185
- )
151
+ fail("Device key does not support signing.")
186
152
  }
187
153
 
188
154
  var error: Unmanaged<CFError>?
@@ -196,12 +162,12 @@ func sign(_ base64Data: String) {
196
162
 
197
163
  if let error {
198
164
  fail(
199
- "Secure Enclave signing failed: " +
165
+ "Signing failed: " +
200
166
  error.takeRetainedValue().localizedDescription
201
167
  )
202
168
  }
203
169
 
204
- fail("Secure Enclave signing failed.")
170
+ fail("Signing failed.")
205
171
  }
206
172
 
207
173
  print(
@@ -210,7 +176,7 @@ func sign(_ base64Data: String) {
210
176
  }
211
177
 
212
178
 
213
- // Remove the DS01 key from this Mac.
179
+ // Delete the DS01 private key from Keychain.
214
180
  func deleteKey() {
215
181
 
216
182
  let query: [String: Any] = [
@@ -221,10 +187,7 @@ func deleteKey() {
221
187
  keyTag.data(using: .utf8)!,
222
188
 
223
189
  kSecAttrKeyType as String:
224
- kSecAttrKeyTypeECSECPrimeRandom,
225
-
226
- kSecAttrTokenID as String:
227
- kSecAttrTokenIDSecureEnclave
190
+ kSecAttrKeyTypeECSECPrimeRandom
228
191
  ]
229
192
 
230
193
  let status = SecItemDelete(
@@ -235,7 +198,7 @@ func deleteKey() {
235
198
  status != errSecItemNotFound {
236
199
 
237
200
  fail(
238
- "Unable to delete DS01 Secure Enclave key."
201
+ "Unable to delete DS01 device key."
239
202
  )
240
203
  }
241
204
 
@@ -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: Uint8Array,
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
- "swift",
306
- args,
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
- data: Uint8Array,
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
- );
@@ -1,19 +0,0 @@
1
- import { webcrypto } from "node:crypto";
2
-
3
- const { subtle } = webcrypto;
4
-
5
- export async function generateDeviceKeyPair() {
6
- // Generate an asymmetric key pair:
7
- // - publicKey → can be shared with the server
8
- // - privateKey → stays on this device
9
- const keyPair = await subtle.generateKey(
10
- {
11
- name: "ECDSA",
12
- namedCurve: "P-256",
13
- },
14
- false, // 🔐 Private key is NON-EXPORTABLE
15
- ["sign", "verify"],
16
- );
17
-
18
- return keyPair;
19
- }