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.
@@ -1,3 +1,4 @@
1
1
  [?9001h[?1004h]0;C:\WINDOWS\system32\cmd.exe[?25h[?25l
2
- > ds-01@1.0.5 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
3
- > tsc[?25h[?9001l[?1004l
2
+ > ds-01@1.0.14 build C:\Web-dev 2.0\Personal Projects\ds01\packages\cli
3
+ > tsc && node scripts/copy-native.js && node scripts/build-macos-helper.js[?25hSkipping macOS native helper build.
4
+ [?9001l[?1004l
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsGpC,eAAO,MAAM,GAAG,SAwTZ,CAAC"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+EpC,eAAO,MAAM,GAAG,SA+LZ,CAAC"}
@@ -9,9 +9,7 @@ import { signWithDeviceKey } from "../utils/secureKeyStore.js";
9
9
  import machineIdPkg from "node-machine-id";
10
10
  const { machineIdSync } = machineIdPkg;
11
11
  const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
12
- // ---------------------------------------------------------
13
- // Helper: Verify Tailwind exists
14
- // ---------------------------------------------------------
12
+ // function for tailwind dependency check
15
13
  function checkTailwindInstallation() {
16
14
  const targetDir = process.cwd();
17
15
  const pkgJsonPath = path.join(targetDir, "package.json");
@@ -34,11 +32,10 @@ function checkTailwindInstallation() {
34
32
  process.exit(1);
35
33
  }
36
34
  }
37
- // ---------------------------------------------------------
38
- // Helper: Find dependencies that are actually missing
39
- // ---------------------------------------------------------
35
+ // function to Find dependencies that are actually missing
40
36
  function getMissingDependencies(dependencies) {
41
37
  const packageJsonPath = path.join(process.cwd(), "package.json");
38
+ // I cannot check what is installed, so treat all required dependencies as missing
42
39
  if (!fs.existsSync(packageJsonPath)) {
43
40
  return dependencies;
44
41
  }
@@ -49,9 +46,6 @@ function getMissingDependencies(dependencies) {
49
46
  };
50
47
  return dependencies.filter((dependency) => !installedDependencies[dependency]);
51
48
  }
52
- // ---------------------------------------------------------
53
- // ADD COMMAND
54
- // ---------------------------------------------------------
55
49
  export const add = new Command()
56
50
  .name("add")
57
51
  .description("Add a component from DS01 to your project")
@@ -59,9 +53,7 @@ export const add = new Command()
59
53
  .action(async (componentName) => {
60
54
  console.log();
61
55
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("Adding Component")}`);
62
- // -----------------------------------------------------
63
- // 1. Validate project
64
- // -----------------------------------------------------
56
+ // validatin
65
57
  checkTailwindInstallation();
66
58
  const cwd = process.cwd();
67
59
  const configPath = path.join(cwd, "ds01.config.json");
@@ -70,9 +62,7 @@ export const add = new Command()
70
62
  process.exit(1);
71
63
  }
72
64
  const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
73
- // -----------------------------------------------------
74
- // 2. Authentication
75
- // -----------------------------------------------------
65
+ // for auth
76
66
  const token = getToken();
77
67
  const machineId = machineIdSync();
78
68
  if (!token || !machineId) {
@@ -82,9 +72,7 @@ export const add = new Command()
82
72
  const s = spinner();
83
73
  s.start(`Fetching ${pc.cyan(`<${componentName} />`)} from registry...`);
84
74
  try {
85
- // --------------------------------------------------
86
- // 3. Create request-specific signed message
87
- // --------------------------------------------------
75
+ // Creating request-specific signed message
88
76
  const timestamp = Date.now().toString();
89
77
  const message = [
90
78
  "GET",
@@ -92,14 +80,10 @@ export const add = new Command()
92
80
  timestamp,
93
81
  machineId,
94
82
  ].join("\n");
95
- // --------------------------------------------------
96
- // 4. Sign request with secure device private key
97
- // --------------------------------------------------
83
+ // Sign request with secure device private key
98
84
  const signature = await signWithDeviceKey(new TextEncoder().encode(message));
99
85
  const signatureBase64 = Buffer.from(signature).toString("base64");
100
- // --------------------------------------------------
101
- // 5. Request component from registry
102
- // --------------------------------------------------
86
+ // Request component from registry
103
87
  const response = await fetch(`${API_BASE_URL}/api/registry/${componentName}`, {
104
88
  method: "GET",
105
89
  headers: {
@@ -109,33 +93,24 @@ export const add = new Command()
109
93
  "X-DS01-Signature": signatureBase64,
110
94
  },
111
95
  });
112
- // --------------------------------------------------
113
- // 6. Handle registry errors
114
- // --------------------------------------------------
96
+ // Handle registry errors
115
97
  if (!response.ok) {
116
98
  const errorData = await response.json().catch(() => null);
117
99
  s.stop(pc.red("Component fetch failed."));
118
- cancel(errorData?.error ||
119
- `Server returned HTTP ${response.status}`);
100
+ cancel(errorData?.error || `Server returned HTTP ${response.status}`);
120
101
  process.exit(1);
121
102
  }
122
103
  const componentData = await response.json();
123
- // --------------------------------------------------
124
- // 7. Create component directory
125
- // --------------------------------------------------
104
+ // Create component directory
126
105
  const targetDir = path.join(cwd, config.componentsPath, componentName);
127
106
  if (!fs.existsSync(targetDir)) {
128
107
  fs.mkdirSync(targetDir, {
129
108
  recursive: true,
130
109
  });
131
110
  }
132
- // --------------------------------------------------
133
- // 8. Extract bundled file names
134
- // --------------------------------------------------
111
+ // Extract bundled file names
135
112
  const bundledFileNames = componentData.files.map((file) => file.name.replace(/\.[^/.]+$/, ""));
136
- // --------------------------------------------------
137
- // 9. Write component files
138
- // --------------------------------------------------
113
+ // Write component files
139
114
  for (const file of componentData.files) {
140
115
  const filePath = path.join(targetDir, file.name);
141
116
  let content = file.content;
@@ -146,9 +121,7 @@ export const add = new Command()
146
121
  fs.writeFileSync(filePath, content);
147
122
  }
148
123
  s.stop(pc.green(`Downloaded ${componentData.files.length} files into ${pc.white(`/${config.componentsPath}/${componentName}`)}`));
149
- // --------------------------------------------------
150
- // 10. Install ONLY missing dependencies
151
- // --------------------------------------------------
124
+ // Install ONLY missing dependencies
152
125
  const registryDependencies = Array.isArray(componentData.dependencies)
153
126
  ? componentData.dependencies
154
127
  : [];
@@ -167,32 +140,14 @@ export const add = new Command()
167
140
  note(`Please run: ${pc.cyan(`npm install ${depsToInstall}`)} manually.`, "Manual Action Required");
168
141
  }
169
142
  }
170
- // --------------------------------------------------
171
- // 11. No dependencies needed
172
- // --------------------------------------------------
173
- // We intentionally don't show a spinner here.
174
- //
175
- // Example:
176
- //
177
- // DS01 requires ["motion"]
178
- // package.json already contains "motion"
179
- //
180
- // → Nothing happens.
181
- // → No npm install.
182
- // → No unnecessary package changes.
183
- // --------------------------------------------------
184
- // 12. Success
185
- // --------------------------------------------------
186
- const mainFile = bundledFileNames.find((name) => name.toLowerCase() ===
187
- componentName.toLowerCase()) || bundledFileNames[0];
143
+ const mainFile = bundledFileNames.find((name) => name.toLowerCase() === componentName.toLowerCase()) || bundledFileNames[0];
188
144
  outro(`${pc.white("✔")} ${pc.bold(`Component <${componentName} /> is ready!`)}\n` +
189
145
  pc.gray("Import it: ") +
190
146
  pc.cyan(`import { ${mainFile} } from "@/${config.componentsPath}/${componentName}/${mainFile}"`));
191
147
  }
192
148
  catch (error) {
193
149
  s.stop(pc.red("An error occurred during injection."));
194
- cancel(pc.gray(error?.message ||
195
- "Unknown CLI Error"));
150
+ cancel(pc.gray(error?.message || "Unknown CLI Error"));
196
151
  process.exit(1);
197
152
  }
198
153
  });
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,IAAI,SA4Fb,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,IAAI,SAmEb,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { intro, outro, spinner, text, isCancel, cancel } from "@clack/prompts";
4
+ import { intro, outro, spinner, cancel } from "@clack/prompts";
5
5
  import chalk from "chalk";
6
6
  export const init = new Command()
7
7
  .name("init")
@@ -13,72 +13,38 @@ export const init = new Command()
13
13
  const cwd = process.cwd();
14
14
  const packageJsonPath = path.join(cwd, "package.json");
15
15
  const tsconfigPath = path.join(cwd, "tsconfig.json");
16
- // 1. Check for package.json
16
+ // runs only if package file missing
17
17
  if (!fs.existsSync(packageJsonPath)) {
18
18
  s.stop("Environment check failed.");
19
19
  cancel("No package.json found. Please run this command in the root of your project.");
20
20
  process.exit(1);
21
21
  }
22
22
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
23
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
24
- // 2. Check strictly for React or Next.js
23
+ const deps = {
24
+ ...packageJson.dependencies,
25
+ ...packageJson.devDependencies,
26
+ };
27
+ // Check strictly for React or Next.js
25
28
  if (!deps.react && !deps.next) {
26
29
  s.stop("Environment check failed.");
27
30
  cancel("DS01 strictly supports React and Next.js environments. Please run this in a supported project.");
28
31
  process.exit(1);
29
32
  }
30
- // 3. Check strictly for TypeScript
33
+ // Check strictly for TypeScript
31
34
  if (!fs.existsSync(tsconfigPath)) {
32
35
  s.stop("Environment check failed.");
33
36
  cancel("DS01 requires TypeScript. No tsconfig.json found in the project root.");
34
37
  process.exit(1);
35
38
  }
36
39
  s.stop(chalk.green("✔ React and TypeScript detected!"));
37
- // 4. Ask the user where their global CSS is located
38
- const cssPath = await text({
39
- message: "Where is your global CSS file?",
40
- placeholder: "app/globals.css",
41
- initialValue: "app/globals.css",
42
- });
43
- if (isCancel(cssPath)) {
44
- cancel("Setup cancelled.");
45
- process.exit(0);
46
- }
47
- // 5. Inject Dark Mode / Glassmorphic CSS variables
48
- const fullCssPath = path.join(cwd, cssPath);
49
- if (fs.existsSync(fullCssPath)) {
50
- const cssContent = fs.readFileSync(fullCssPath, "utf-8");
51
- if (!cssContent.includes("/* ds01-theme-start */")) {
52
- const variables = `
53
- /* ds01-theme-start */
54
- @layer base {
55
- :root {
56
- --ds-background: #0a0a0a; /* Deep charcoal */
57
- --ds-foreground: #ffffff;
58
- --ds-glass-border: rgba(255, 255, 255, 0.08);
59
- --ds-glass-blur: blur(16px);
60
- }
61
- }
62
- /* ds01-theme-end */
63
- `;
64
- fs.appendFileSync(fullCssPath, variables);
65
- console.log(chalk.green(`✔ Injected design tokens into ${cssPath}`));
66
- }
67
- else {
68
- console.log(chalk.gray(`- Design tokens already exist in ${cssPath}`));
69
- }
70
- }
71
- else {
72
- console.log(chalk.yellow(`⚠ Could not find ${cssPath}. You may need to add the CSS variables manually.`));
73
- }
74
- // 6. Create the local configuration file so the CLI remembers this project
40
+ // Create the local configuration file so the CLI remembers this project
41
+ //! Need to be understand
75
42
  const configPath = path.join(cwd, "ds01.config.json");
76
43
  const configData = {
77
44
  $schema: "https://ds-01.vercel.app/schema.json",
78
45
  style: "default",
79
46
  typescript: true,
80
- cssPath: cssPath,
81
- componentsPath: "components/ui" // This is where we will inject components later
47
+ componentsPath: "components/ui", // This is where we will inject components later
82
48
  };
83
49
  fs.writeFileSync(configPath, JSON.stringify(configData, null, 2));
84
50
  console.log(chalk.green(`✔ Created ds01.config.json`));
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,KAAK,SA6Td,CAAC"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,eAAO,MAAM,KAAK,SAyJd,CAAC"}
@@ -1,12 +1,11 @@
1
1
  import { Command } from "commander";
2
- import { intro, outro, spinner, note, cancel, } from "@clack/prompts";
2
+ import { intro, outro, spinner, note, cancel } from "@clack/prompts";
3
3
  import pc from "picocolors";
4
4
  import open from "open";
5
5
  import machineIdPkg from "node-machine-id";
6
6
  import { saveToken } from "../utils/config.js";
7
7
  import { createDeviceKey, getDevicePublicKey, } from "../utils/secureKeyStore.js";
8
- const API_BASE_URL = process.env.DS01_API_URL ||
9
- "https://ds-01.vercel.app";
8
+ const API_BASE_URL = process.env.DS01_API_URL || "https://ds-01.vercel.app";
10
9
  const { machineIdSync } = machineIdPkg;
11
10
  export const login = new Command()
12
11
  .name("login")
@@ -16,19 +15,12 @@ export const login = new Command()
16
15
  intro(`${pc.bgWhite(pc.black(pc.bold(" DS01 ")))} ${pc.gray("v1.0.0 — Terminal Authentication")}`);
17
16
  const s = spinner();
18
17
  try {
19
- // ==================================================
20
- // 1. CREATE / LOAD SECURE DEVICE KEY
21
- // ==================================================
22
18
  s.start("Preparing secure device identity...");
23
19
  await createDeviceKey();
24
20
  const publicKey = await getDevicePublicKey();
25
21
  const hardwareId = machineIdSync();
26
- const publicKeyType = process.platform === "win32"
27
- ? "windows-rsa"
28
- : "macos-ec";
29
- // ==================================================
30
- // 2. REQUEST DEVICE CODE
31
- // ==================================================
22
+ const publicKeyType = process.platform === "win32" ? "windows-rsa" : "macos-ec";
23
+ // Req to my server with machineId and certificate for now but eventually will send the publicKey
32
24
  s.start("Requesting pairing code...");
33
25
  const initResponse = await fetch(`${API_BASE_URL}/api/auth/device/code`, {
34
26
  method: "POST",
@@ -46,16 +38,12 @@ export const login = new Command()
46
38
  cancel(`Server responded with HTTP ${initResponse.status}`);
47
39
  process.exit(1);
48
40
  }
49
- const { deviceCode, userCode, verificationUrl, interval, } = await initResponse.json();
50
- if (!deviceCode ||
51
- !userCode ||
52
- !verificationUrl) {
41
+ const { deviceCode, userCode, verificationUrl, interval } = await initResponse.json();
42
+ if (!deviceCode || !userCode || !verificationUrl) {
53
43
  throw new Error("Authorization server returned an invalid device response.");
54
44
  }
55
45
  s.stop(pc.green("Secure device identity registered."));
56
- // ==================================================
57
- // 3. SHOW BROWSER AUTHORIZATION
58
- // ==================================================
46
+ // Opening users browser with verification URL
59
47
  note(`${pc.bold("Pairing Code:")} ${pc.cyan(pc.bold(` ${userCode} `))}\n` +
60
48
  `${pc.bold("Verification URL:")} ${pc.underline(verificationUrl)}`, "Action Required");
61
49
  try {
@@ -64,9 +52,7 @@ export const login = new Command()
64
52
  catch {
65
53
  // Browser launch failure is non-fatal.
66
54
  }
67
- // ==================================================
68
- // 4. POLL FOR AUTHORIZATION
69
- // ==================================================
55
+ // we polling to one of my server endpoint to check wether the temporary req is approved or not ??
70
56
  s.start("Waiting for web authorization...");
71
57
  const pollInterval = (interval || 5) * 1000;
72
58
  let token = null;
@@ -84,65 +70,40 @@ export const login = new Command()
84
70
  });
85
71
  let tokenData;
86
72
  try {
87
- tokenData =
88
- await tokenResponse.json();
73
+ tokenData = await tokenResponse.json();
89
74
  }
90
75
  catch {
91
76
  s.stop(pc.red("Authorization server returned invalid JSON."));
92
77
  cancel("Unable to read authorization response.");
93
78
  process.exit(1);
94
79
  }
95
- // ==================================================
96
- // AUTHORIZATION SUCCESS
97
- // ==================================================
80
+ // Auth success stop polling
98
81
  if (tokenResponse.ok &&
99
- typeof tokenData.accessToken ===
100
- "string" &&
82
+ typeof tokenData.accessToken === "string" &&
101
83
  tokenData.accessToken.length > 0) {
102
- token =
103
- tokenData.accessToken;
84
+ token = tokenData.accessToken;
104
85
  break;
105
86
  }
106
- // ==================================================
107
- // STILL WAITING
108
- // ==================================================
109
- if (tokenData.error ===
110
- "authorization_pending") {
87
+ // Still waiting keep polling
88
+ if (tokenData.error === "authorization_pending") {
111
89
  continue;
112
90
  }
113
- // ==================================================
114
- // AUTHORIZATION FAILED
115
- // ==================================================
91
+ // Authorization Failed
116
92
  s.stop(pc.red("Authorization failed or expired."));
117
- cancel(`Reason: ${tokenData.error ||
118
- "Unknown authorization error"}`);
93
+ cancel(`Reason: ${tokenData.error || "Unknown authorization error"}`);
119
94
  process.exit(1);
120
95
  }
121
- // ==================================================
122
- // 5. STOP POLLING SPINNER
123
- // ==================================================
96
+ // outside the loop stop the spinner
124
97
  s.stop(pc.green("Hardware signature linked & token verified!"));
125
- // ==================================================
126
- // 6. ENSURE TOKEN EXISTS
127
- // ==================================================
98
+ // ENSURE TOKEN EXISTS
128
99
  if (!token) {
129
100
  throw new Error("Authorization completed without receiving an access token.");
130
101
  }
131
- // ==================================================
132
- // 7. SAVE TOKEN
133
- // ==================================================
134
102
  saveToken(token, hardwareId);
135
- // ==================================================
136
- // 8. SUCCESS
137
- // ==================================================
138
103
  outro(`${pc.white("✔")} ${pc.bold("Terminal synced successfully")}\n` +
139
104
  pc.gray("Secure device key and session token are ready."));
140
- // ==================================================
141
- // 9. HARD EXIT
142
- // ==================================================
143
- //
105
+ // Hard Exit
144
106
  // The login command is completely finished.
145
- //
146
107
  // This prevents any lingering CLI handles
147
108
  // (spinner timers, readline handles, etc.)
148
109
  // from keeping the Node process alive.
@@ -151,8 +112,7 @@ export const login = new Command()
151
112
  }
152
113
  catch (error) {
153
114
  s.stop(pc.red("An error occurred during login."));
154
- cancel(pc.gray(error?.message ||
155
- "Unknown CLI Error"));
115
+ cancel(pc.gray(error?.message || "Unknown CLI Error"));
156
116
  process.exit(1);
157
117
  }
158
118
  });
@@ -1 +1 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,MAAM,SAiDf,CAAC"}
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,MAAM,SA+Cf,CAAC"}
@@ -32,9 +32,7 @@ export const logout = new Command()
32
32
  }
33
33
  // NEW: Delete local credentials only after the server confirms
34
34
  // that the CLI session has been revoked.
35
- console.log("✅ Server confirmed CLI session was revoked.");
36
35
  deleteToken();
37
- console.log("✅ Local credentials deleted.");
38
36
  spinner.succeed(chalk.green("Successfully logged out."));
39
37
  console.log(chalk.gray("Your CLI session has been revoked and local credentials have been cleared."));
40
38
  }
@@ -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