@futdevpro/fsm-dynamo 1.11.21 → 1.11.23
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/build/_modules/crypto/_collections/crypto.util.d.ts +7 -4
- package/build/_modules/crypto/_collections/crypto.util.d.ts.map +1 -1
- package/build/_modules/crypto/_collections/crypto.util.js +20 -10
- package/build/_modules/crypto/_collections/crypto.util.js.map +1 -1
- package/build/_modules/crypto/_collections/crypto.util.spec.js +260 -77
- package/build/_modules/crypto/_collections/crypto.util.spec.js.map +1 -1
- package/futdevpro-fsm-dynamo-01.11.23.tgz +0 -0
- package/package.json +7 -5
- package/scripts/crypto/CRYPTO-STABILITY-SOLUTION.md +197 -0
- package/scripts/crypto/README.md +114 -0
- package/scripts/crypto/demo-crypto-stability.js +121 -0
- package/scripts/crypto/stress-test-crypto.js +380 -0
- package/src/_modules/crypto/_collections/crypto.util.spec.ts +224 -1
- package/src/_modules/crypto/_collections/crypto.util.ts +20 -10
- package/futdevpro-fsm-dynamo-01.11.21.tgz +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crypto Stress Test - Valódi Világ Szimuláció
|
|
3
|
+
* Teszteli a legkülönbözőbb edge case-eket és hosszú távú stabilitást
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { DyFM_Crypto } = require('../../build/_modules/crypto/index.js');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
|
|
9
|
+
console.log('🔥 Crypto Stress Test - Valódi Világ Szimuláció\n');
|
|
10
|
+
|
|
11
|
+
// Test 1: Különböző adattípusok és méretek
|
|
12
|
+
console.log('1️⃣ Adattípus és Méret Stress Test:');
|
|
13
|
+
const dataTypes = [
|
|
14
|
+
// Primitív típusok
|
|
15
|
+
'simple string',
|
|
16
|
+
42,
|
|
17
|
+
true,
|
|
18
|
+
null,
|
|
19
|
+
|
|
20
|
+
// Komplex objektumok
|
|
21
|
+
{ id: 1, name: 'test' },
|
|
22
|
+
[1, 2, 3, 'array'],
|
|
23
|
+
{ nested: { deep: { value: true } } },
|
|
24
|
+
|
|
25
|
+
// Nagy adatok
|
|
26
|
+
{ data: 'x'.repeat(10000) }, // 10KB string
|
|
27
|
+
{ array: Array.from({length: 1000}, (_, i) => i) }, // 1000 elemű tömb
|
|
28
|
+
|
|
29
|
+
// Különleges karakterek
|
|
30
|
+
{ hungarian: 'árvíztűrő tükörfúrógép' },
|
|
31
|
+
{ chinese: '你好世界' },
|
|
32
|
+
{ emoji: '🚀💻🔒🎯' },
|
|
33
|
+
{ special: '!@#$%^&*()_+-=[]{}|;:,.<>?' },
|
|
34
|
+
|
|
35
|
+
// Edge case-ek
|
|
36
|
+
{ empty: {} },
|
|
37
|
+
{ nulls: { a: null, b: null, c: null } },
|
|
38
|
+
{ mixed: [null, '', 0, false, 'string', 123, true] } // Removed undefined as it gets filtered out
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const testKey = 'stress-test-key-123';
|
|
42
|
+
let allDataTypesPassed = true;
|
|
43
|
+
|
|
44
|
+
dataTypes.forEach((data, index) => {
|
|
45
|
+
try {
|
|
46
|
+
console.log(` Testing ${index + 1}: ${typeof data} - ${JSON.stringify(data).substring(0, 50)}...`);
|
|
47
|
+
const encrypted = DyFM_Crypto.encrypt(data, testKey);
|
|
48
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
49
|
+
|
|
50
|
+
// JSON.stringify összehasonlítás (null/undefined kezelés miatt)
|
|
51
|
+
const originalStr = JSON.stringify(data);
|
|
52
|
+
const decryptedStr = JSON.stringify(decrypted);
|
|
53
|
+
|
|
54
|
+
if (originalStr !== decryptedStr) {
|
|
55
|
+
console.log(` ❌ Test ${index + 1} failed: ${typeof data}`);
|
|
56
|
+
console.log(` Original: ${originalStr.substring(0, 100)}...`);
|
|
57
|
+
console.log(` Decrypted: ${decryptedStr.substring(0, 100)}...`);
|
|
58
|
+
allDataTypesPassed = false;
|
|
59
|
+
} else {
|
|
60
|
+
console.log(` ✅ Test ${index + 1} passed: ${typeof data}`);
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.log(` ❌ Test ${index + 1} crashed: ${typeof data} - ${error.message}`);
|
|
64
|
+
console.log(` Data: ${JSON.stringify(data).substring(0, 100)}...`);
|
|
65
|
+
allDataTypesPassed = false;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
console.log(` Adattípusok teszt: ${allDataTypesPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
70
|
+
|
|
71
|
+
// Test 2: Hosszú távú stabilitás szimuláció
|
|
72
|
+
console.log('\n2️⃣ Hosszú Távú Stabilitás Szimuláció:');
|
|
73
|
+
const longTermData = { id: 1, name: 'long-term-test', timestamp: new Date().toISOString() };
|
|
74
|
+
const longTermKey = 'long-term-key-123';
|
|
75
|
+
|
|
76
|
+
// 10,000 iteráció (valós életben ez évek lehet)
|
|
77
|
+
const iterations = 10000;
|
|
78
|
+
let longTermPassed = true;
|
|
79
|
+
let lastEncrypted = null;
|
|
80
|
+
|
|
81
|
+
console.log(` Futtatás ${iterations.toLocaleString()} iteráció...`);
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < iterations; i++) {
|
|
84
|
+
try {
|
|
85
|
+
const encrypted = DyFM_Crypto.encrypt(longTermData, longTermKey);
|
|
86
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, longTermKey);
|
|
87
|
+
|
|
88
|
+
// Ellenőrizzük, hogy mindig ugyanazt kapjuk-e
|
|
89
|
+
if (lastEncrypted && lastEncrypted !== encrypted) {
|
|
90
|
+
console.log(` ❌ Inkonzisztens eredmény iteráció ${i + 1}-nél`);
|
|
91
|
+
longTermPassed = false;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Ellenőrizzük, hogy helyesen dekódolódik-e
|
|
96
|
+
if (JSON.stringify(decrypted) !== JSON.stringify(longTermData)) {
|
|
97
|
+
console.log(` ❌ Dekódolási hiba iteráció ${i + 1}-nél`);
|
|
98
|
+
longTermPassed = false;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
lastEncrypted = encrypted;
|
|
103
|
+
|
|
104
|
+
// Progress jelzés
|
|
105
|
+
if ((i + 1) % 1000 === 0) {
|
|
106
|
+
process.stdout.write(` ${i + 1}/${iterations} ✅\r`);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log(`\n ❌ Hiba iteráció ${i + 1}-nél: ${error.message}`);
|
|
110
|
+
longTermPassed = false;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`\n Hosszú távú stabilitás: ${longTermPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
116
|
+
|
|
117
|
+
// Test 3: Adatbázis szimuláció
|
|
118
|
+
console.log('\n3️⃣ Adatbázis Szimuláció:');
|
|
119
|
+
const dbScenarios = [
|
|
120
|
+
// MongoDB dokumentum
|
|
121
|
+
{
|
|
122
|
+
_id: '507f1f77bcf86cd799439011',
|
|
123
|
+
username: 'testuser',
|
|
124
|
+
email: 'test@example.com',
|
|
125
|
+
profile: {
|
|
126
|
+
firstName: 'John',
|
|
127
|
+
lastName: 'Doe',
|
|
128
|
+
age: 30,
|
|
129
|
+
preferences: {
|
|
130
|
+
theme: 'dark',
|
|
131
|
+
language: 'hu',
|
|
132
|
+
notifications: true
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
createdAt: new Date().toISOString(),
|
|
136
|
+
updatedAt: new Date().toISOString(),
|
|
137
|
+
__v: 0
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// PostgreSQL rekord
|
|
141
|
+
{
|
|
142
|
+
id: 1,
|
|
143
|
+
name: 'test_record',
|
|
144
|
+
description: 'This is a test record with special chars: áéíóöőúüű',
|
|
145
|
+
metadata: {
|
|
146
|
+
tags: ['test', 'demo', 'crypto'],
|
|
147
|
+
flags: [true, false, true],
|
|
148
|
+
numbers: [1, 2, 3, 4, 5]
|
|
149
|
+
},
|
|
150
|
+
created_at: '2024-01-01T00:00:00.000Z',
|
|
151
|
+
updated_at: '2024-12-31T23:59:59.999Z'
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// MySQL rekord
|
|
155
|
+
{
|
|
156
|
+
user_id: 12345,
|
|
157
|
+
username: 'mysql_user',
|
|
158
|
+
password_hash: 'hashed_password_123',
|
|
159
|
+
email: 'mysql@example.com',
|
|
160
|
+
is_active: 1,
|
|
161
|
+
last_login: '2024-12-31 23:59:59',
|
|
162
|
+
settings: JSON.stringify({
|
|
163
|
+
timezone: 'Europe/Budapest',
|
|
164
|
+
currency: 'HUF',
|
|
165
|
+
date_format: 'YYYY-MM-DD'
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
let dbTestPassed = true;
|
|
171
|
+
dbScenarios.forEach((scenario, index) => {
|
|
172
|
+
try {
|
|
173
|
+
const encrypted = DyFM_Crypto.encrypt(scenario, testKey);
|
|
174
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
175
|
+
|
|
176
|
+
// Részletes összehasonlítás
|
|
177
|
+
const originalStr = JSON.stringify(scenario);
|
|
178
|
+
const decryptedStr = JSON.stringify(decrypted);
|
|
179
|
+
|
|
180
|
+
if (originalStr !== decryptedStr) {
|
|
181
|
+
console.log(` ❌ DB scenario ${index + 1} failed`);
|
|
182
|
+
console.log(` Original keys: ${Object.keys(scenario).join(', ')}`);
|
|
183
|
+
console.log(` Decrypted keys: ${Object.keys(decrypted).join(', ')}`);
|
|
184
|
+
dbTestPassed = false;
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.log(` ❌ DB scenario ${index + 1} crashed: ${error.message}`);
|
|
188
|
+
dbTestPassed = false;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
console.log(` Adatbázis szimuláció: ${dbTestPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
193
|
+
|
|
194
|
+
// Test 4: Hálózati kommunikáció szimuláció
|
|
195
|
+
console.log('\n4️⃣ Hálózati Kommunikáció Szimuláció:');
|
|
196
|
+
const networkScenarios = [
|
|
197
|
+
// HTTP Header
|
|
198
|
+
{
|
|
199
|
+
type: 'http_header',
|
|
200
|
+
data: {
|
|
201
|
+
'Authorization': 'Bearer token123',
|
|
202
|
+
'X-User-ID': 'user_456',
|
|
203
|
+
'X-Session-ID': 'session_789',
|
|
204
|
+
'X-Request-ID': crypto.randomUUID(),
|
|
205
|
+
'X-Timestamp': new Date().toISOString()
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// WebSocket üzenet
|
|
210
|
+
{
|
|
211
|
+
type: 'websocket_message',
|
|
212
|
+
data: {
|
|
213
|
+
event: 'user_action',
|
|
214
|
+
payload: {
|
|
215
|
+
action: 'button_click',
|
|
216
|
+
button_id: 'submit_form',
|
|
217
|
+
timestamp: Date.now(),
|
|
218
|
+
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
219
|
+
},
|
|
220
|
+
metadata: {
|
|
221
|
+
version: '1.0.0',
|
|
222
|
+
source: 'web_client',
|
|
223
|
+
correlation_id: crypto.randomUUID()
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
// API Response
|
|
229
|
+
{
|
|
230
|
+
type: 'api_response',
|
|
231
|
+
data: {
|
|
232
|
+
success: true,
|
|
233
|
+
data: {
|
|
234
|
+
users: [
|
|
235
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com' },
|
|
236
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com' },
|
|
237
|
+
{ id: 3, name: 'Charlie', email: 'charlie@example.com' }
|
|
238
|
+
],
|
|
239
|
+
pagination: {
|
|
240
|
+
page: 1,
|
|
241
|
+
limit: 10,
|
|
242
|
+
total: 3,
|
|
243
|
+
has_next: false
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
timestamp: new Date().toISOString(),
|
|
247
|
+
request_id: crypto.randomUUID()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
let networkTestPassed = true;
|
|
253
|
+
networkScenarios.forEach((scenario, index) => {
|
|
254
|
+
try {
|
|
255
|
+
const encrypted = DyFM_Crypto.encrypt(scenario, testKey);
|
|
256
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
257
|
+
|
|
258
|
+
const originalStr = JSON.stringify(scenario);
|
|
259
|
+
const decryptedStr = JSON.stringify(decrypted);
|
|
260
|
+
|
|
261
|
+
if (originalStr !== decryptedStr) {
|
|
262
|
+
console.log(` ❌ Network scenario ${index + 1} failed: ${scenario.type}`);
|
|
263
|
+
networkTestPassed = false;
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.log(` ❌ Network scenario ${index + 1} crashed: ${error.message}`);
|
|
267
|
+
networkTestPassed = false;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
console.log(` Hálózati kommunikáció: ${networkTestPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
272
|
+
|
|
273
|
+
// Test 5: Különböző kulcsok és konfigurációk
|
|
274
|
+
console.log('\n5️⃣ Különböző Kulcsok és Konfigurációk:');
|
|
275
|
+
const keyScenarios = [
|
|
276
|
+
{ key: 'simple-key', config: undefined },
|
|
277
|
+
{ key: 'a'.repeat(100), config: undefined }, // 100 karakter hosszú
|
|
278
|
+
{ key: 'special-chars!@#$%^&*()', config: undefined },
|
|
279
|
+
{ key: 'unicode-ключ-世界', config: undefined },
|
|
280
|
+
{ key: 'normal-key', config: { keyIterations: 500, keySize: 6 } },
|
|
281
|
+
{ key: 'fast-key', config: { keyIterations: 100, keySize: 4 } },
|
|
282
|
+
{ key: 'secure-key', config: { keyIterations: 2000, keySize: 10 } }
|
|
283
|
+
];
|
|
284
|
+
|
|
285
|
+
let keyTestPassed = true;
|
|
286
|
+
const testData = { id: 1, name: 'key-test' };
|
|
287
|
+
|
|
288
|
+
keyScenarios.forEach((scenario, index) => {
|
|
289
|
+
try {
|
|
290
|
+
const encrypted = DyFM_Crypto.encrypt(testData, scenario.key, scenario.config);
|
|
291
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, scenario.key, scenario.config);
|
|
292
|
+
|
|
293
|
+
if (JSON.stringify(decrypted) !== JSON.stringify(testData)) {
|
|
294
|
+
console.log(` ❌ Key scenario ${index + 1} failed: ${scenario.key.substring(0, 20)}...`);
|
|
295
|
+
keyTestPassed = false;
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.log(` ❌ Key scenario ${index + 1} crashed: ${error.message}`);
|
|
299
|
+
keyTestPassed = false;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
console.log(` Kulcs és konfiguráció teszt: ${keyTestPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
304
|
+
|
|
305
|
+
// Test 6: Memória és teljesítmény stress test
|
|
306
|
+
console.log('\n6️⃣ Memória és Teljesítmény Stress Test:');
|
|
307
|
+
const memoryData = { id: 1, data: 'x'.repeat(1000) }; // 1KB adat
|
|
308
|
+
const memoryKey = 'memory-test-key';
|
|
309
|
+
const memoryIterations = 10000;
|
|
310
|
+
|
|
311
|
+
console.log(` Futtatás ${memoryIterations.toLocaleString()} iteráció ${(memoryData.data.length / 1024).toFixed(1)}KB adattal...`);
|
|
312
|
+
|
|
313
|
+
const startTime = Date.now();
|
|
314
|
+
let memoryTestPassed = true;
|
|
315
|
+
const encryptedResults = [];
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
for (let i = 0; i < memoryIterations; i++) {
|
|
319
|
+
const encrypted = DyFM_Crypto.encrypt(memoryData, memoryKey);
|
|
320
|
+
encryptedResults.push(encrypted);
|
|
321
|
+
|
|
322
|
+
// Minden 1000. iterációban ellenőrizzük a memóriát
|
|
323
|
+
if ((i + 1) % 1000 === 0) {
|
|
324
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, memoryKey);
|
|
325
|
+
if (JSON.stringify(decrypted) !== JSON.stringify(memoryData)) {
|
|
326
|
+
console.log(` ❌ Memória teszt hiba iteráció ${i + 1}-nél`);
|
|
327
|
+
memoryTestPassed = false;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
process.stdout.write(` ${i + 1}/${memoryIterations} ✅\r`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.log(`\n ❌ Memória teszt crash: ${error.message}`);
|
|
335
|
+
memoryTestPassed = false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const endTime = Date.now();
|
|
339
|
+
const duration = endTime - startTime;
|
|
340
|
+
const memoryUsage = process.memoryUsage();
|
|
341
|
+
|
|
342
|
+
console.log(`\n Memória teszt: ${memoryTestPassed ? '✅ PASS' : '❌ FAIL'}`);
|
|
343
|
+
console.log(` Időtartam: ${duration}ms`);
|
|
344
|
+
console.log(` Memória használat: ${(memoryUsage.heapUsed / 1024 / 1024).toFixed(2)}MB`);
|
|
345
|
+
|
|
346
|
+
// Összesítés
|
|
347
|
+
console.log('\n🎯 STRESS TEST ÖSSZESÍTÉS:');
|
|
348
|
+
console.log('========================');
|
|
349
|
+
|
|
350
|
+
const allTests = [
|
|
351
|
+
{ name: 'Adattípusok', passed: allDataTypesPassed },
|
|
352
|
+
{ name: 'Hosszú távú stabilitás', passed: longTermPassed },
|
|
353
|
+
{ name: 'Adatbázis szimuláció', passed: dbTestPassed },
|
|
354
|
+
{ name: 'Hálózati kommunikáció', passed: networkTestPassed },
|
|
355
|
+
{ name: 'Kulcsok és konfigurációk', passed: keyTestPassed },
|
|
356
|
+
{ name: 'Memória és teljesítmény', passed: memoryTestPassed }
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
const passedTests = allTests.filter(test => test.passed).length;
|
|
360
|
+
const totalTests = allTests.length;
|
|
361
|
+
|
|
362
|
+
allTests.forEach(test => {
|
|
363
|
+
console.log(` ${test.passed ? '✅' : '❌'} ${test.name}`);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
console.log(`\n📊 Eredmény: ${passedTests}/${totalTests} teszt sikeres`);
|
|
367
|
+
console.log(` Sikeresség: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
|
|
368
|
+
|
|
369
|
+
if (passedTests === totalTests) {
|
|
370
|
+
console.log('\n🎉 MINDEN TESZT SIKERES!');
|
|
371
|
+
console.log(' A kriptográfiai implementáció valóban stabil és megbízható!');
|
|
372
|
+
console.log(' Biztonságosan használható hosszú távon különböző rendszerek között.');
|
|
373
|
+
} else {
|
|
374
|
+
console.log('\n⚠️ FIGYELEM: Néhány teszt nem sikerült!');
|
|
375
|
+
console.log(' A kriptográfiai implementáció további finomhangolásra szorul.');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Memória tisztítás
|
|
379
|
+
encryptedResults.length = 0;
|
|
380
|
+
global.gc && global.gc(); // Garbage collection (ha elérhető)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DyFM_Crypto } from './crypto
|
|
1
|
+
import { DyFM_Crypto } from './crypto.util';
|
|
2
2
|
|
|
3
3
|
describe('| DyFM_Crypto', () => {
|
|
4
4
|
const testKey = 'test-secret-key-123';
|
|
@@ -148,6 +148,229 @@ describe('| DyFM_Crypto', () => {
|
|
|
148
148
|
});
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
describe('| LONG-TERM STABILITY TESTS - Cross-platform compatibility', () => {
|
|
152
|
+
describe('| Deterministic encryption consistency', () => {
|
|
153
|
+
it('| should produce identical encrypted output for identical input across multiple calls', () => {
|
|
154
|
+
const testData = { id: 1, name: 'test', timestamp: '2024-01-01T00:00:00Z' };
|
|
155
|
+
const key = 'stable-test-key-123';
|
|
156
|
+
|
|
157
|
+
// Multiple encryption calls should produce identical results
|
|
158
|
+
const encrypted1 = DyFM_Crypto.encrypt(testData, key);
|
|
159
|
+
const encrypted2 = DyFM_Crypto.encrypt(testData, key);
|
|
160
|
+
const encrypted3 = DyFM_Crypto.encrypt(testData, key);
|
|
161
|
+
|
|
162
|
+
expect(encrypted1).toBe(encrypted2);
|
|
163
|
+
expect(encrypted2).toBe(encrypted3);
|
|
164
|
+
expect(encrypted1).toBe(encrypted3);
|
|
165
|
+
|
|
166
|
+
// All should decrypt to the same data
|
|
167
|
+
const decrypted1 = DyFM_Crypto.decrypt(encrypted1, key);
|
|
168
|
+
const decrypted2 = DyFM_Crypto.decrypt(encrypted2, key);
|
|
169
|
+
const decrypted3 = DyFM_Crypto.decrypt(encrypted3, key);
|
|
170
|
+
|
|
171
|
+
expect(decrypted1).toEqual(decrypted2);
|
|
172
|
+
expect(decrypted2).toEqual(decrypted3);
|
|
173
|
+
expect(decrypted1).toEqual(testData);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('| should maintain consistency with different data types and structures', () => {
|
|
177
|
+
const testCases = [
|
|
178
|
+
{ data: { id: 1, name: 'simple' }, key: 'key1' },
|
|
179
|
+
{ data: 'plain string', key: 'key2' },
|
|
180
|
+
{ data: [1, 2, 3, 'array'], key: 'key3' },
|
|
181
|
+
{ data: { nested: { deep: { value: true } } }, key: 'key4' },
|
|
182
|
+
{ data: { empty: {}, null: null }, key: 'key5' } // Removed undefined as it gets filtered out
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
testCases.forEach(({ data, key }) => {
|
|
186
|
+
const encrypted1 = DyFM_Crypto.encrypt(data, key);
|
|
187
|
+
const encrypted2 = DyFM_Crypto.encrypt(data, key);
|
|
188
|
+
expect(encrypted1).toBe(encrypted2);
|
|
189
|
+
|
|
190
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted1, key);
|
|
191
|
+
expect(decrypted).toEqual(data);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('| should handle edge cases consistently', () => {
|
|
196
|
+
const edgeCases = [
|
|
197
|
+
{ data: '', key: 'empty-string-key' },
|
|
198
|
+
{ data: 0, key: 'zero-number-key' },
|
|
199
|
+
{ data: false, key: 'false-boolean-key' },
|
|
200
|
+
{ data: { a: 0, b: false, c: '' }, key: 'mixed-edge-key' }
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
edgeCases.forEach(({ data, key }) => {
|
|
204
|
+
const encrypted1 = DyFM_Crypto.encrypt(data, key);
|
|
205
|
+
const encrypted2 = DyFM_Crypto.encrypt(data, key);
|
|
206
|
+
expect(encrypted1).toBe(encrypted2);
|
|
207
|
+
|
|
208
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted1, key);
|
|
209
|
+
expect(decrypted).toEqual(data);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('| Cross-version compatibility simulation', () => {
|
|
215
|
+
it('| should handle data encrypted with different configurations consistently', () => {
|
|
216
|
+
const testData = { id: 1, name: 'config-test' };
|
|
217
|
+
const key = 'config-test-key';
|
|
218
|
+
|
|
219
|
+
// Test with default config
|
|
220
|
+
const defaultEncrypted = DyFM_Crypto.encrypt(testData, key);
|
|
221
|
+
const defaultDecrypted = DyFM_Crypto.decrypt(defaultEncrypted, key);
|
|
222
|
+
expect(defaultDecrypted).toEqual(testData);
|
|
223
|
+
|
|
224
|
+
// Test with custom config
|
|
225
|
+
const customConfig = { keyIterations: 500, keySize: 6 };
|
|
226
|
+
const customEncrypted = DyFM_Crypto.encrypt(testData, key, customConfig);
|
|
227
|
+
const customDecrypted = DyFM_Crypto.decrypt(customEncrypted, key, customConfig);
|
|
228
|
+
expect(customDecrypted).toEqual(testData);
|
|
229
|
+
|
|
230
|
+
// Test that different configs produce different encrypted results
|
|
231
|
+
expect(defaultEncrypted).not.toBe(customEncrypted);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('| should maintain backward compatibility with existing encrypted data', () => {
|
|
235
|
+
// Simulate data that was encrypted with previous version
|
|
236
|
+
const legacyData = { id: 1, name: 'legacy-test', version: '1.0' };
|
|
237
|
+
const key = 'legacy-key';
|
|
238
|
+
|
|
239
|
+
// Encrypt with current implementation
|
|
240
|
+
const encrypted = DyFM_Crypto.encrypt(legacyData, key);
|
|
241
|
+
|
|
242
|
+
// Simulate system restart/reload
|
|
243
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, key);
|
|
244
|
+
expect(decrypted).toEqual(legacyData);
|
|
245
|
+
|
|
246
|
+
// Re-encrypt should produce same result
|
|
247
|
+
const reEncrypted = DyFM_Crypto.encrypt(decrypted, key);
|
|
248
|
+
expect(reEncrypted).toBe(encrypted);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('| Platform independence tests', () => {
|
|
253
|
+
it('| should handle different character encodings consistently', () => {
|
|
254
|
+
const unicodeData = {
|
|
255
|
+
hungarian: 'árvíztűrő tükörfúrógép',
|
|
256
|
+
chinese: '你好世界',
|
|
257
|
+
emoji: '🚀💻🔒',
|
|
258
|
+
special: '!@#$%^&*()_+-=[]{}|;:,.<>?'
|
|
259
|
+
};
|
|
260
|
+
const key = 'unicode-test-key';
|
|
261
|
+
|
|
262
|
+
const encrypted = DyFM_Crypto.encrypt(unicodeData, key);
|
|
263
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, key);
|
|
264
|
+
expect(decrypted).toEqual(unicodeData);
|
|
265
|
+
|
|
266
|
+
// Multiple encryptions should be identical
|
|
267
|
+
const encrypted2 = DyFM_Crypto.encrypt(unicodeData, key);
|
|
268
|
+
expect(encrypted).toBe(encrypted2);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('| should handle large data objects consistently', () => {
|
|
272
|
+
// Create a large object with many properties
|
|
273
|
+
const largeData: any = { id: 1 };
|
|
274
|
+
for (let i = 0; i < 100; i++) {
|
|
275
|
+
largeData[`prop${i}`] = `value${i}`;
|
|
276
|
+
}
|
|
277
|
+
const key = 'large-data-key';
|
|
278
|
+
|
|
279
|
+
const encrypted1 = DyFM_Crypto.encrypt(largeData, key);
|
|
280
|
+
const encrypted2 = DyFM_Crypto.encrypt(largeData, key);
|
|
281
|
+
expect(encrypted1).toBe(encrypted2);
|
|
282
|
+
|
|
283
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted1, key);
|
|
284
|
+
expect(decrypted).toEqual(largeData);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('| should handle nested circular references gracefully', () => {
|
|
288
|
+
const circularData: any = { id: 1, name: 'circular' };
|
|
289
|
+
circularData.self = circularData;
|
|
290
|
+
const key = 'circular-key';
|
|
291
|
+
|
|
292
|
+
// Should handle circular references without crashing
|
|
293
|
+
expect(() => {
|
|
294
|
+
const encrypted = DyFM_Crypto.encrypt(circularData, key);
|
|
295
|
+
// Note: JSON.stringify will fail on circular refs, so this test ensures graceful handling
|
|
296
|
+
}).toThrow(); // Expected to throw due to circular reference
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('| Time-based stability tests', () => {
|
|
301
|
+
it('| should maintain consistency across time-based operations', () => {
|
|
302
|
+
const timeData = {
|
|
303
|
+
timestamp: new Date().toISOString(),
|
|
304
|
+
id: 1,
|
|
305
|
+
name: 'time-test'
|
|
306
|
+
};
|
|
307
|
+
const key = 'time-test-key';
|
|
308
|
+
|
|
309
|
+
// Encrypt same data multiple times
|
|
310
|
+
const encrypted1 = DyFM_Crypto.encrypt(timeData, key);
|
|
311
|
+
|
|
312
|
+
// Simulate time passing
|
|
313
|
+
setTimeout(() => {
|
|
314
|
+
const encrypted2 = DyFM_Crypto.encrypt(timeData, key);
|
|
315
|
+
expect(encrypted1).toBe(encrypted2);
|
|
316
|
+
}, 100);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('| should handle date objects consistently', () => {
|
|
320
|
+
const dateData = {
|
|
321
|
+
created: new Date('2024-01-01T00:00:00Z'),
|
|
322
|
+
updated: new Date('2024-12-31T23:59:59Z'),
|
|
323
|
+
id: 1
|
|
324
|
+
};
|
|
325
|
+
const key = 'date-test-key';
|
|
326
|
+
|
|
327
|
+
const encrypted1 = DyFM_Crypto.encrypt(dateData, key);
|
|
328
|
+
const encrypted2 = DyFM_Crypto.encrypt(dateData, key);
|
|
329
|
+
expect(encrypted1).toBe(encrypted2);
|
|
330
|
+
|
|
331
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted1, key) as any;
|
|
332
|
+
// Compare ISO strings since JSON serialization converts dates to strings
|
|
333
|
+
expect(decrypted.created).toEqual(dateData.created.toISOString());
|
|
334
|
+
expect(decrypted.updated).toEqual(dateData.updated.toISOString());
|
|
335
|
+
expect(decrypted.id).toBe(dateData.id);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('| Memory and performance stability', () => {
|
|
340
|
+
it('| should handle multiple encryption/decryption cycles without memory leaks', () => {
|
|
341
|
+
const testData = { id: 1, name: 'memory-test' };
|
|
342
|
+
const key = 'memory-test-key';
|
|
343
|
+
|
|
344
|
+
// Perform many encryption/decryption cycles
|
|
345
|
+
for (let i = 0; i < 1000; i++) {
|
|
346
|
+
const encrypted = DyFM_Crypto.encrypt(testData, key);
|
|
347
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, key);
|
|
348
|
+
expect(decrypted).toEqual(testData);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('| should maintain consistent performance across multiple operations', () => {
|
|
353
|
+
const testData = { id: 1, name: 'performance-test' };
|
|
354
|
+
const key = 'performance-test-key';
|
|
355
|
+
|
|
356
|
+
const startTime = Date.now();
|
|
357
|
+
|
|
358
|
+
// Perform multiple operations
|
|
359
|
+
for (let i = 0; i < 100; i++) {
|
|
360
|
+
const encrypted = DyFM_Crypto.encrypt(testData, key);
|
|
361
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, key);
|
|
362
|
+
expect(decrypted).toEqual(testData);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const endTime = Date.now();
|
|
366
|
+
const duration = endTime - startTime;
|
|
367
|
+
|
|
368
|
+
// Should complete within reasonable time (adjust threshold as needed)
|
|
369
|
+
expect(duration).toBeLessThan(5000); // 5 seconds max
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
151
374
|
describe('| edge cases', () => {
|
|
152
375
|
describe('| empty and null values', () => {
|
|
153
376
|
it('| should handle empty objects', () => {
|
|
@@ -27,14 +27,15 @@ export interface CryptoConfig {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* A utility class for
|
|
31
|
-
* Uses AES-256-CBC with
|
|
30
|
+
* A utility class for stable encryption and decryption of data
|
|
31
|
+
* Uses AES-256-CBC with deterministic IV and salt for consistent results across systems
|
|
32
|
+
* Prioritizes reliability and cross-platform compatibility over security
|
|
32
33
|
*/
|
|
33
34
|
export class DyFM_Crypto {
|
|
34
35
|
private static readonly DEFAULT_CONFIG: Required<CryptoConfig> = {
|
|
35
36
|
ivLength: 16, // 128 bits
|
|
36
37
|
saltLength: 16, // 128 bits
|
|
37
|
-
keyIterations:
|
|
38
|
+
keyIterations: 1000, // Reduced for better performance and stability
|
|
38
39
|
keySize: 8 // 256 bits (8 * 32)
|
|
39
40
|
};
|
|
40
41
|
private static readonly defaultErrorUserMsg =
|
|
@@ -58,7 +59,8 @@ export class DyFM_Crypto {
|
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
// Allow null values but not undefined
|
|
63
|
+
if (data === undefined) {
|
|
62
64
|
throw new DyFM_Error({
|
|
63
65
|
...this.getDefaultErrorSettings('validateInput'),
|
|
64
66
|
errorCode: 'DyFM-CRY-IDT',
|
|
@@ -69,22 +71,30 @@ export class DyFM_Crypto {
|
|
|
69
71
|
|
|
70
72
|
/**
|
|
71
73
|
* Generates a deterministic IV based on the input data and key
|
|
74
|
+
* Uses MD5 for better stability across different CryptoJS versions
|
|
72
75
|
*/
|
|
73
76
|
private static generateIV(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
// Use MD5 for better stability - simpler hash algorithm, more consistent across versions
|
|
78
|
+
const combined = data + key;
|
|
79
|
+
const hash = CryptoJS.MD5(combined);
|
|
80
|
+
// Use slice(0, 4) for 16 bytes - more stable than division operations
|
|
81
|
+
return CryptoJS.lib.WordArray.create(hash.words.slice(0, 4));
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/**
|
|
79
85
|
* Generates a deterministic salt based on the input data and key
|
|
86
|
+
* Uses MD5 for better stability across different CryptoJS versions
|
|
80
87
|
*/
|
|
81
88
|
private static generateSalt(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
// Use MD5 for better stability - simpler hash algorithm, more consistent across versions
|
|
90
|
+
const combined = key + data;
|
|
91
|
+
const hash = CryptoJS.MD5(combined);
|
|
92
|
+
// Use slice(0, 4) for 16 bytes - more stable than division operations
|
|
93
|
+
return CryptoJS.lib.WordArray.create(hash.words.slice(0, 4));
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
/**
|
|
87
|
-
* Derives a key using PBKDF2
|
|
97
|
+
* Derives a key using PBKDF2 with reduced iterations for stability
|
|
88
98
|
*/
|
|
89
99
|
private static deriveKey(key: string, salt: CryptoJS.lib.WordArray, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
|
|
90
100
|
return CryptoJS.PBKDF2(key, salt, {
|
|
@@ -141,7 +151,7 @@ export class DyFM_Crypto {
|
|
|
141
151
|
}
|
|
142
152
|
|
|
143
153
|
/**
|
|
144
|
-
* Encrypts data using AES-256-CBC
|
|
154
|
+
* Encrypts data using AES-256-CBC with deterministic IV and salt
|
|
145
155
|
* @param data The data to encrypt
|
|
146
156
|
* @param key The encryption key
|
|
147
157
|
* @param config Optional configuration
|
|
Binary file
|