profile-manager-secure 1.0.0

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.
Files changed (70) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +107 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/frontend/assets/index-D0IIAYWB.css +1 -0
  5. package/dist/frontend/assets/index-D9Y0_s3b.js +273 -0
  6. package/dist/frontend/index.html +26 -0
  7. package/dist/middleware/auth.middleware.d.ts +8 -0
  8. package/dist/middleware/auth.middleware.js +51 -0
  9. package/dist/middleware/auth.middleware.js.map +1 -0
  10. package/dist/middleware/error.middleware.d.ts +2 -0
  11. package/dist/middleware/error.middleware.js +11 -0
  12. package/dist/middleware/error.middleware.js.map +1 -0
  13. package/dist/modules/auth/auth.controller.d.ts +1 -0
  14. package/dist/modules/auth/auth.controller.js +175 -0
  15. package/dist/modules/auth/auth.controller.js.map +1 -0
  16. package/dist/modules/credential/credential.controller.d.ts +1 -0
  17. package/dist/modules/credential/credential.controller.js +263 -0
  18. package/dist/modules/credential/credential.controller.js.map +1 -0
  19. package/dist/modules/proxy/proxy.controller.d.ts +1 -0
  20. package/dist/modules/proxy/proxy.controller.js +270 -0
  21. package/dist/modules/proxy/proxy.controller.js.map +1 -0
  22. package/dist/modules/proxy/proxy.service.d.ts +28 -0
  23. package/dist/modules/proxy/proxy.service.js +185 -0
  24. package/dist/modules/proxy/proxy.service.js.map +1 -0
  25. package/dist/modules/tailscale/tailscale.controller.d.ts +1 -0
  26. package/dist/modules/tailscale/tailscale.controller.js +149 -0
  27. package/dist/modules/tailscale/tailscale.controller.js.map +1 -0
  28. package/dist/modules/tailscale/tailscale.service.d.ts +96 -0
  29. package/dist/modules/tailscale/tailscale.service.js +561 -0
  30. package/dist/modules/tailscale/tailscale.service.js.map +1 -0
  31. package/dist/modules/totp/totp.controller.d.ts +1 -0
  32. package/dist/modules/totp/totp.controller.js +41 -0
  33. package/dist/modules/totp/totp.controller.js.map +1 -0
  34. package/dist/modules/totp/totp.service.d.ts +26 -0
  35. package/dist/modules/totp/totp.service.js +96 -0
  36. package/dist/modules/totp/totp.service.js.map +1 -0
  37. package/dist/modules/totp/totp.service.test.d.ts +1 -0
  38. package/dist/modules/totp/totp.service.test.js +41 -0
  39. package/dist/modules/totp/totp.service.test.js.map +1 -0
  40. package/dist/modules/vault/apikey.service.d.ts +27 -0
  41. package/dist/modules/vault/apikey.service.js +87 -0
  42. package/dist/modules/vault/apikey.service.js.map +1 -0
  43. package/dist/modules/vault/constants.d.ts +2 -0
  44. package/dist/modules/vault/constants.js +24 -0
  45. package/dist/modules/vault/constants.js.map +1 -0
  46. package/dist/modules/vault/types.d.ts +73 -0
  47. package/dist/modules/vault/types.js +2 -0
  48. package/dist/modules/vault/types.js.map +1 -0
  49. package/dist/modules/vault/vault-migration.service.d.ts +21 -0
  50. package/dist/modules/vault/vault-migration.service.js +99 -0
  51. package/dist/modules/vault/vault-migration.service.js.map +1 -0
  52. package/dist/modules/vault/vault-storage.service.d.ts +22 -0
  53. package/dist/modules/vault/vault-storage.service.js +76 -0
  54. package/dist/modules/vault/vault-storage.service.js.map +1 -0
  55. package/dist/modules/vault/vault.service.d.ts +66 -0
  56. package/dist/modules/vault/vault.service.js +229 -0
  57. package/dist/modules/vault/vault.service.js.map +1 -0
  58. package/dist/server.d.ts +1 -0
  59. package/dist/server.js +67 -0
  60. package/dist/server.js.map +1 -0
  61. package/dist/utils/crypto.d.ts +20 -0
  62. package/dist/utils/crypto.js +54 -0
  63. package/dist/utils/crypto.js.map +1 -0
  64. package/dist/utils/crypto.test.d.ts +1 -0
  65. package/dist/utils/crypto.test.js +66 -0
  66. package/dist/utils/crypto.test.js.map +1 -0
  67. package/dist/utils/logger.d.ts +5 -0
  68. package/dist/utils/logger.js +12 -0
  69. package/dist/utils/logger.js.map +1 -0
  70. package/package.json +38 -0
@@ -0,0 +1,22 @@
1
+ export declare class VaultStorageService {
2
+ /**
3
+ * Check if the vault file exists
4
+ */
5
+ exists(vaultPath: string): Promise<boolean>;
6
+ /**
7
+ * Get the password hint from the raw vault file without decrypting its contents
8
+ */
9
+ getHint(vaultPath: string): Promise<string | null>;
10
+ /**
11
+ * Read raw content from the vault file
12
+ */
13
+ read(vaultPath: string): Promise<{
14
+ salt: string;
15
+ ciphertext: string;
16
+ hint?: string;
17
+ }>;
18
+ /**
19
+ * Write content to the vault file atomically after creating a backup of the existing file
20
+ */
21
+ write(vaultPath: string, fileContent: string): Promise<void>;
22
+ }
@@ -0,0 +1,76 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { logger } from '../../utils/logger.js';
4
+ export class VaultStorageService {
5
+ /**
6
+ * Check if the vault file exists
7
+ */
8
+ async exists(vaultPath) {
9
+ try {
10
+ await fs.access(vaultPath);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ /**
18
+ * Get the password hint from the raw vault file without decrypting its contents
19
+ */
20
+ async getHint(vaultPath) {
21
+ if (!(await this.exists(vaultPath))) {
22
+ return null;
23
+ }
24
+ try {
25
+ const rawContent = await fs.readFile(vaultPath, 'utf8');
26
+ const parsed = JSON.parse(rawContent);
27
+ return parsed.hint || null;
28
+ }
29
+ catch (error) {
30
+ logger.error('Failed to read password hint from vault file:', error.message);
31
+ return null;
32
+ }
33
+ }
34
+ /**
35
+ * Read raw content from the vault file
36
+ */
37
+ async read(vaultPath) {
38
+ try {
39
+ const rawContent = await fs.readFile(vaultPath, 'utf8');
40
+ return JSON.parse(rawContent);
41
+ }
42
+ catch (error) {
43
+ logger.error(`Failed to read vault file at ${vaultPath}:`, error.message);
44
+ throw error;
45
+ }
46
+ }
47
+ /**
48
+ * Write content to the vault file atomically after creating a backup of the existing file
49
+ */
50
+ async write(vaultPath, fileContent) {
51
+ try {
52
+ // 1. Create a backup of the existing vault file if it exists
53
+ if (await this.exists(vaultPath)) {
54
+ const backupPath = `${vaultPath}.bak`;
55
+ try {
56
+ await fs.copyFile(vaultPath, backupPath);
57
+ logger.info(`Vault backup created at ${backupPath}`);
58
+ }
59
+ catch (backupError) {
60
+ logger.warn(`Failed to create vault backup: ${backupError.message}. Proceeding with save.`);
61
+ }
62
+ }
63
+ // 2. Write to a temporary file and rename it to target path for atomic update
64
+ const dir = path.dirname(vaultPath);
65
+ await fs.mkdir(dir, { recursive: true });
66
+ const tmpPath = `${vaultPath}.tmp`;
67
+ await fs.writeFile(tmpPath, fileContent, 'utf8');
68
+ await fs.rename(tmpPath, vaultPath);
69
+ }
70
+ catch (error) {
71
+ logger.error(`Failed to write vault file at ${vaultPath}:`, error.message);
72
+ throw error;
73
+ }
74
+ }
75
+ }
76
+ //# sourceMappingURL=vault-storage.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-storage.service.js","sourceRoot":"","sources":["../../../src/modules/vault/vault-storage.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { Category, Platform, CustomField, Credential, ApiKey, Proxy, VaultData } from './types.js';
2
+ export type { Category, Platform, CustomField, Credential, ApiKey, Proxy, VaultData };
3
+ declare class VaultService {
4
+ private get vaultPath();
5
+ private isUnlocked;
6
+ private encryptionKey;
7
+ private vaultData;
8
+ private apiKeysService;
9
+ private storageService;
10
+ private migrationService;
11
+ constructor();
12
+ /**
13
+ * Check if vault file exists
14
+ */
15
+ exists(): Promise<boolean>;
16
+ /**
17
+ * Check if the vault is unlocked
18
+ */
19
+ unlocked(): boolean;
20
+ /**
21
+ * Get decrypted vault data (only if unlocked)
22
+ */
23
+ getData(): VaultData;
24
+ /**
25
+ * Initialize a new vault with a master password
26
+ */
27
+ initialize(password: string, hint?: string): Promise<void>;
28
+ /**
29
+ * Unlock the vault with the master password
30
+ */
31
+ unlock(password: string): Promise<boolean>;
32
+ /**
33
+ * Lock the vault and wipe memory
34
+ */
35
+ lock(): void;
36
+ /**
37
+ * Get password hint from vault file without unlocking
38
+ */
39
+ getHint(): Promise<string | null>;
40
+ /**
41
+ * Save the in-memory vault data back to disk encrypted
42
+ */
43
+ save(): Promise<void>;
44
+ /**
45
+ * Change master password and re-encrypt vault database
46
+ */
47
+ changePassword(currentPassword: string, newPassword: string, hint?: string): Promise<boolean>;
48
+ /**
49
+ * Get vault stats and storage file path info
50
+ */
51
+ getVaultInfo(): {
52
+ path: string;
53
+ credentialsCount: number;
54
+ categoriesCount: number;
55
+ };
56
+ getApiKeys(): ApiKey[];
57
+ createApiKey(name: string): Promise<string>;
58
+ toggleApiKey(id: string, enabled: boolean): Promise<void>;
59
+ deleteApiKey(id: string): Promise<void>;
60
+ validateApiKey(key: string): boolean;
61
+ /**
62
+ * Automatically initializes the vault with default password '180823' if it does not exist
63
+ */
64
+ autoInitializeIfMissing(): Promise<void>;
65
+ }
66
+ export declare const vaultService: VaultService;
@@ -0,0 +1,229 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import { deriveKey, encrypt, decrypt, generateSalt, hashPassword } from '../../utils/crypto.js';
4
+ import { logger } from '../../utils/logger.js';
5
+ import { defaultPlatforms } from './constants.js';
6
+ import { ApiKeyService } from './apikey.service.js';
7
+ import { VaultStorageService } from './vault-storage.service.js';
8
+ import { VaultMigrationService } from './vault-migration.service.js';
9
+ class VaultService {
10
+ get vaultPath() {
11
+ const defaultDir = path.join(os.homedir(), '.account-manager');
12
+ return process.env.DB_PATH || path.join(defaultDir, 'vault.enc');
13
+ }
14
+ isUnlocked = false;
15
+ encryptionKey = null;
16
+ vaultData = null;
17
+ apiKeysService;
18
+ storageService;
19
+ migrationService;
20
+ constructor() {
21
+ this.storageService = new VaultStorageService();
22
+ this.migrationService = new VaultMigrationService();
23
+ this.apiKeysService = new ApiKeyService(() => this.getData(), () => this.save(), () => this.unlocked());
24
+ }
25
+ /**
26
+ * Check if vault file exists
27
+ */
28
+ async exists() {
29
+ return this.storageService.exists(this.vaultPath);
30
+ }
31
+ /**
32
+ * Check if the vault is unlocked
33
+ */
34
+ unlocked() {
35
+ return this.isUnlocked;
36
+ }
37
+ /**
38
+ * Get decrypted vault data (only if unlocked)
39
+ */
40
+ getData() {
41
+ if (!this.isUnlocked || !this.vaultData) {
42
+ throw new Error('Vault is locked');
43
+ }
44
+ return this.vaultData;
45
+ }
46
+ /**
47
+ * Initialize a new vault with a master password
48
+ */
49
+ async initialize(password, hint) {
50
+ if (await this.exists()) {
51
+ throw new Error('Vault already exists');
52
+ }
53
+ logger.info('Initializing new vault...');
54
+ const salt = generateSalt();
55
+ const storageSalt = generateSalt();
56
+ const passwordHash = hashPassword(password, storageSalt);
57
+ const initialData = {
58
+ schemaVersion: 2, // Initialize with latest version
59
+ user: {
60
+ password_hash: passwordHash,
61
+ password_salt: storageSalt,
62
+ password_hint: hint
63
+ },
64
+ categories: [],
65
+ credentials: [],
66
+ platforms: defaultPlatforms
67
+ };
68
+ // Derive key and encrypt
69
+ const key = deriveKey(password, salt);
70
+ const ciphertext = encrypt(JSON.stringify(initialData), key);
71
+ // Save file
72
+ const fileContent = JSON.stringify({
73
+ salt,
74
+ ciphertext,
75
+ hint
76
+ });
77
+ await this.storageService.write(this.vaultPath, fileContent);
78
+ // Auto-unlock
79
+ this.encryptionKey = key;
80
+ this.vaultData = initialData;
81
+ this.isUnlocked = true;
82
+ logger.info('Vault initialized and unlocked successfully');
83
+ }
84
+ /**
85
+ * Unlock the vault with the master password
86
+ */
87
+ async unlock(password) {
88
+ if (!(await this.exists())) {
89
+ throw new Error('Vault has not been initialized');
90
+ }
91
+ try {
92
+ const { salt, ciphertext } = await this.storageService.read(this.vaultPath);
93
+ const key = deriveKey(password, salt);
94
+ const decryptedDataRaw = decrypt(ciphertext, key);
95
+ const data = JSON.parse(decryptedDataRaw);
96
+ // Verify password hash for double safety
97
+ const computedHash = hashPassword(password, data.user.password_salt);
98
+ if (computedHash !== data.user.password_hash) {
99
+ logger.warn('Password hash verification failed');
100
+ return false;
101
+ }
102
+ this.encryptionKey = key;
103
+ this.vaultData = data;
104
+ this.isUnlocked = true;
105
+ // Run sequential migrations
106
+ const { migratedData, needsSave } = await this.migrationService.migrate(this.vaultData);
107
+ this.vaultData = migratedData;
108
+ if (needsSave) {
109
+ await this.save();
110
+ }
111
+ logger.info('Vault unlocked successfully');
112
+ return true;
113
+ }
114
+ catch (error) {
115
+ logger.error('Failed to unlock vault:', error.message);
116
+ return false;
117
+ }
118
+ }
119
+ /**
120
+ * Lock the vault and wipe memory
121
+ */
122
+ lock() {
123
+ this.isUnlocked = false;
124
+ this.encryptionKey = null;
125
+ this.vaultData = null;
126
+ logger.info('Vault locked');
127
+ }
128
+ /**
129
+ * Get password hint from vault file without unlocking
130
+ */
131
+ async getHint() {
132
+ return this.storageService.getHint(this.vaultPath);
133
+ }
134
+ /**
135
+ * Save the in-memory vault data back to disk encrypted
136
+ */
137
+ async save() {
138
+ if (!this.isUnlocked || !this.encryptionKey || !this.vaultData) {
139
+ throw new Error('Vault is locked, cannot save');
140
+ }
141
+ const { salt } = await this.storageService.read(this.vaultPath);
142
+ const ciphertext = encrypt(JSON.stringify(this.vaultData), this.encryptionKey);
143
+ const fileContent = JSON.stringify({
144
+ salt,
145
+ ciphertext,
146
+ hint: this.vaultData.user.password_hint
147
+ });
148
+ await this.storageService.write(this.vaultPath, fileContent);
149
+ }
150
+ /**
151
+ * Change master password and re-encrypt vault database
152
+ */
153
+ async changePassword(currentPassword, newPassword, hint) {
154
+ if (!this.isUnlocked || !this.encryptionKey || !this.vaultData) {
155
+ throw new Error('Vault is locked');
156
+ }
157
+ // 1. Verify current password hash
158
+ const computedHash = hashPassword(currentPassword, this.vaultData.user.password_salt);
159
+ if (computedHash !== this.vaultData.user.password_hash) {
160
+ logger.warn('Change password failed: current password incorrect');
161
+ return false;
162
+ }
163
+ // 2. Generate new storage salt and hash new password
164
+ const newStorageSalt = generateSalt();
165
+ const newPasswordHash = hashPassword(newPassword, newStorageSalt);
166
+ // 3. Update vaultData user info
167
+ this.vaultData.user = {
168
+ password_hash: newPasswordHash,
169
+ password_salt: newStorageSalt,
170
+ password_hint: hint
171
+ };
172
+ // 4. Generate new salt for file encryption and derive new key
173
+ const newSalt = generateSalt();
174
+ const newKey = deriveKey(newPassword, newSalt);
175
+ // 5. Encrypt vault data with the new key
176
+ const ciphertext = encrypt(JSON.stringify(this.vaultData), newKey);
177
+ const fileContent = JSON.stringify({
178
+ salt: newSalt,
179
+ ciphertext,
180
+ hint
181
+ });
182
+ // 6. Save to disk atomically
183
+ await this.storageService.write(this.vaultPath, fileContent);
184
+ // 7. Update in-memory keys
185
+ this.encryptionKey = newKey;
186
+ logger.info('Master password changed successfully');
187
+ return true;
188
+ }
189
+ /**
190
+ * Get vault stats and storage file path info
191
+ */
192
+ getVaultInfo() {
193
+ if (!this.isUnlocked || !this.vaultData) {
194
+ throw new Error('Vault is locked');
195
+ }
196
+ return {
197
+ path: this.vaultPath,
198
+ credentialsCount: this.vaultData.credentials.length,
199
+ categoriesCount: this.vaultData.categories.length
200
+ };
201
+ }
202
+ getApiKeys() {
203
+ return this.apiKeysService.getApiKeys();
204
+ }
205
+ async createApiKey(name) {
206
+ return this.apiKeysService.createApiKey(name);
207
+ }
208
+ async toggleApiKey(id, enabled) {
209
+ return this.apiKeysService.toggleApiKey(id, enabled);
210
+ }
211
+ async deleteApiKey(id) {
212
+ return this.apiKeysService.deleteApiKey(id);
213
+ }
214
+ validateApiKey(key) {
215
+ return this.apiKeysService.validateApiKey(key);
216
+ }
217
+ /**
218
+ * Automatically initializes the vault with default password '180823' if it does not exist
219
+ */
220
+ async autoInitializeIfMissing() {
221
+ if (!(await this.exists())) {
222
+ logger.info('Vault database not found. Auto-initializing with default password...');
223
+ await this.initialize('180823', 'Mật khẩu mặc định là 180823');
224
+ this.lock(); // Lock it immediately so the user has to unlock it!
225
+ }
226
+ }
227
+ }
228
+ export const vaultService = new VaultService();
229
+ //# sourceMappingURL=vault.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.service.js","sourceRoot":"","sources":["../../../src/modules/vault/vault.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function startServer(port: number, callback: () => void): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
package/dist/server.js ADDED
@@ -0,0 +1,67 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import helmet from 'helmet';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { authRouter } from './modules/auth/auth.controller.js';
7
+ import { credentialRouter } from './modules/credential/credential.controller.js';
8
+ import { tailscaleRouter } from './modules/tailscale/tailscale.controller.js';
9
+ import { proxyRouter } from './modules/proxy/proxy.controller.js';
10
+ import { totpRouter } from './modules/totp/totp.controller.js';
11
+ import { authMiddleware } from './middleware/auth.middleware.js';
12
+ import { errorMiddleware } from './middleware/error.middleware.js';
13
+ import { logger } from './utils/logger.js';
14
+ import { vaultService } from './modules/vault/vault.service.js';
15
+ import fs from 'fs';
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ const app = express();
19
+ // Apply global middlewares
20
+ app.use(helmet({
21
+ contentSecurityPolicy: false, // Disabled to allow loading external platform icons and dynamic assets
22
+ crossOriginEmbedderPolicy: false,
23
+ crossOriginResourcePolicy: false
24
+ }));
25
+ app.use(cors());
26
+ app.use(express.json({ limit: '10mb' }));
27
+ // Request logging middleware
28
+ app.use((req, res, next) => {
29
+ logger.info(`${req.method} ${req.path}`);
30
+ next();
31
+ });
32
+ // API Routes
33
+ app.use('/api/auth', authRouter);
34
+ app.use('/api/credentials', authMiddleware, credentialRouter);
35
+ app.use('/api/tailscale', authMiddleware, tailscaleRouter);
36
+ app.use('/api/proxy', authMiddleware, proxyRouter);
37
+ app.use('/api/totp', authMiddleware, totpRouter);
38
+ // Serve Frontend Static Assets
39
+ const frontendDistPath = fs.existsSync(path.resolve(__dirname, './frontend'))
40
+ ? path.resolve(__dirname, './frontend')
41
+ : path.resolve(__dirname, '../../frontend/dist');
42
+ app.use(express.static(frontendDistPath));
43
+ // Wildcard handler for Single Page Application (React Router)
44
+ app.get('*', (req, res) => {
45
+ res.sendFile(path.join(frontendDistPath, 'index.html'));
46
+ });
47
+ // Global Error Handler
48
+ app.use(errorMiddleware);
49
+ export async function startServer(port, callback) {
50
+ logger.info('Starting server in HTTP mode');
51
+ try {
52
+ await vaultService.autoInitializeIfMissing();
53
+ }
54
+ catch (error) {
55
+ logger.error('Failed to auto-initialize vault:', error);
56
+ }
57
+ return app.listen(port, callback);
58
+ }
59
+ // Support running directly (e.g. during development with tsx)
60
+ if (process.argv[1] && (process.argv[1].endsWith('server.ts') || process.argv[1].endsWith('server.js'))) {
61
+ const port = parseInt(process.env.PORT || '18823', 10);
62
+ startServer(port, () => {
63
+ logger.info(`Server running directly on port ${port}`);
64
+ });
65
+ }
66
+ // Trigger restart
67
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Derive an encryption key from the master password and a salt
3
+ */
4
+ export declare function deriveKey(password: string, salt: string): Buffer;
5
+ /**
6
+ * Encrypt plain text using AES-256-GCM with a derived key
7
+ */
8
+ export declare function encrypt(text: string, key: Buffer): string;
9
+ /**
10
+ * Decrypt cipher text using AES-256-GCM with a derived key
11
+ */
12
+ export declare function decrypt(encryptedText: string, key: Buffer): string;
13
+ /**
14
+ * Generate a cryptographically secure random salt
15
+ */
16
+ export declare function generateSalt(): string;
17
+ /**
18
+ * Hash a password for storage verification (different salt/iterations from encryption key)
19
+ */
20
+ export declare function hashPassword(password: string, salt: string): string;
@@ -0,0 +1,54 @@
1
+ import crypto from 'crypto';
2
+ const PBKDF2_ITERATIONS = 100000;
3
+ const KEY_LEN = 32; // 256 bits
4
+ const DIGEST = 'sha256';
5
+ /**
6
+ * Derive an encryption key from the master password and a salt
7
+ */
8
+ export function deriveKey(password, salt) {
9
+ return crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, KEY_LEN, DIGEST);
10
+ }
11
+ /**
12
+ * Encrypt plain text using AES-256-GCM with a derived key
13
+ */
14
+ export function encrypt(text, key) {
15
+ const iv = crypto.randomBytes(12);
16
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
17
+ let encrypted = cipher.update(text, 'utf8', 'hex');
18
+ encrypted += cipher.final('hex');
19
+ const authTag = cipher.getAuthTag().toString('hex');
20
+ // Format: iv:authTag:ciphertext
21
+ return `${iv.toString('hex')}:${authTag}:${encrypted}`;
22
+ }
23
+ /**
24
+ * Decrypt cipher text using AES-256-GCM with a derived key
25
+ */
26
+ export function decrypt(encryptedText, key) {
27
+ const parts = encryptedText.split(':');
28
+ if (parts.length !== 3) {
29
+ throw new Error('Invalid encrypted text format');
30
+ }
31
+ const iv = Buffer.from(parts[0], 'hex');
32
+ const authTag = Buffer.from(parts[1], 'hex');
33
+ const ciphertext = Buffer.from(parts[2], 'hex');
34
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
35
+ decipher.setAuthTag(authTag);
36
+ let decrypted = decipher.update(ciphertext);
37
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
38
+ return decrypted.toString('utf8');
39
+ }
40
+ /**
41
+ * Generate a cryptographically secure random salt
42
+ */
43
+ export function generateSalt() {
44
+ return crypto.randomBytes(16).toString('hex');
45
+ }
46
+ /**
47
+ * Hash a password for storage verification (different salt/iterations from encryption key)
48
+ */
49
+ export function hashPassword(password, salt) {
50
+ // Use more iterations for storage hashing
51
+ const hash = crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS * 2, KEY_LEN, DIGEST);
52
+ return hash.toString('hex');
53
+ }
54
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,WAAW;AAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC;AAExB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAY;IACtD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,GAAW;IAC/C,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAE7D,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEjC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpD,gCAAgC;IAChC,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,aAAqB,EAAE,GAAW;IACxD,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAEhD,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACjE,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAE7B,IAAI,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5C,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAEzD,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAY;IACzD,0CAA0C;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,iBAAiB,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvF,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ import { deriveKey, encrypt, decrypt, generateSalt, hashPassword } from './crypto.js';
2
+ import { logger } from './logger.js';
3
+ function runTests() {
4
+ logger.info('Starting unit tests for crypto module...');
5
+ try {
6
+ const password = 'myMasterPassword123!';
7
+ const salt = generateSalt();
8
+ // Test 1: Key Derivation
9
+ logger.info('Test 1: Testing key derivation...');
10
+ const key1 = deriveKey(password, salt);
11
+ const key2 = deriveKey(password, salt);
12
+ if (key1.toString('hex') !== key2.toString('hex')) {
13
+ throw new Error('Key derivation is not deterministic');
14
+ }
15
+ logger.info('Test 1 Passed: Key derivation is deterministic and matches.');
16
+ // Test 2: Symmetric Encryption & Decryption (AES-256-GCM)
17
+ logger.info('Test 2: Testing encryption & decryption...');
18
+ const originalText = 'Highly secret credentials: username=admin, password=secret';
19
+ const ciphertext = encrypt(originalText, key1);
20
+ logger.info(`Ciphertext format (iv:tag:cipher): ${ciphertext.substring(0, 40)}...`);
21
+ const decryptedText = decrypt(ciphertext, key1);
22
+ if (decryptedText !== originalText) {
23
+ throw new Error(`Decrypted text does not match original! Expected: "${originalText}", Got: "${decryptedText}"`);
24
+ }
25
+ logger.info('Test 2 Passed: Decryption recovers original text.');
26
+ // Test 3: Authenticated Tamper Proofing
27
+ logger.info('Test 3: Testing decryption integrity with tampered ciphertext...');
28
+ const parts = ciphertext.split(':');
29
+ // Tamper with the ciphertext content
30
+ parts[2] = parts[2].substring(0, parts[2].length - 2) + (parts[2].endsWith('a') ? 'b' : 'a');
31
+ const tamperedCiphertext = parts.join(':');
32
+ try {
33
+ decrypt(tamperedCiphertext, key1);
34
+ throw new Error('Decryption did not fail on tampered ciphertext (integrity check failed)');
35
+ }
36
+ catch (err) {
37
+ if (err.message.includes('Unsupported state or key size') || err.message.includes('BAD_DECRYPT') || err.message.includes('Unsupported state') || err.message.includes('integrity') || err.message.includes('Unsupported') || err.message.includes('AuthTag') || err.message.includes('decrypt') || err.message.includes('MAC')) {
38
+ logger.info('Test 3 Passed: Tampered ciphertext was successfully rejected (throws error).');
39
+ }
40
+ else {
41
+ // GCM decryption throw is generic (Unsupported state or key size / decrypt pending etc)
42
+ logger.info(`Test 3 Passed: Rejected with error: ${err.message}`);
43
+ }
44
+ }
45
+ // Test 4: Password Hashing
46
+ logger.info('Test 4: Testing storage password hashing...');
47
+ const storageSalt = generateSalt();
48
+ const hash1 = hashPassword(password, storageSalt);
49
+ const hash2 = hashPassword(password, storageSalt);
50
+ const hash3 = hashPassword('differentPassword', storageSalt);
51
+ if (hash1 !== hash2) {
52
+ throw new Error('Password hashing is not deterministic');
53
+ }
54
+ if (hash1 === hash3) {
55
+ throw new Error('Different passwords produced the same hash');
56
+ }
57
+ logger.info('Test 4 Passed: Password hashing works as expected.');
58
+ logger.info('🎉 All crypto unit tests completed successfully!');
59
+ }
60
+ catch (error) {
61
+ logger.error('❌ Crypto unit test failed:', error.message);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ runTests();
66
+ //# sourceMappingURL=crypto.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.js","sourceRoot":"","sources":["../../src/utils/crypto.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare const logger: {
2
+ info: (message: string, ...args: any[]) => void;
3
+ warn: (message: string, ...args: any[]) => void;
4
+ error: (message: string, ...args: any[]) => void;
5
+ };