@futdevpro/fsm-dynamo 1.10.31 → 1.10.33
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 +86 -0
- package/build/_modules/crypto/_collections/crypto.util.d.ts.map +1 -0
- package/build/_modules/crypto/_collections/crypto.util.js +212 -0
- package/build/_modules/crypto/_collections/crypto.util.js.map +1 -0
- package/build/_modules/crypto/_collections/crypto.util.spec.d.ts +2 -0
- package/build/_modules/crypto/_collections/crypto.util.spec.d.ts.map +1 -0
- package/build/_modules/crypto/_collections/crypto.util.spec.js +165 -0
- package/build/_modules/crypto/_collections/crypto.util.spec.js.map +1 -0
- package/build/_modules/crypto/index.d.ts +2 -0
- package/build/_modules/crypto/index.d.ts.map +1 -0
- package/build/_modules/crypto/index.js +5 -0
- package/build/_modules/crypto/index.js.map +1 -0
- package/futdevpro-fsm-dynamo-01.10.33.tgz +0 -0
- package/nodemon.json +1 -1
- package/package.json +13 -2
- package/src/_modules/crypto/_collections/crypto.util.spec.ts +195 -0
- package/src/_modules/crypto/_collections/crypto.util.ts +248 -0
- package/src/_modules/crypto/index.ts +3 -0
- package/futdevpro-fsm-dynamo-01.10.31.tgz +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error codes for crypto operations
|
|
3
|
+
*/
|
|
4
|
+
export declare enum CryptoErrorCode {
|
|
5
|
+
INVALID_INPUT = "DyFM-CRY-EA0",
|
|
6
|
+
DECRYPTION_FAILED = "DyFM-CRY-DRY",
|
|
7
|
+
ENCRYPTION_FAILED = "DyFM-CRY-ENF",
|
|
8
|
+
INVALID_KEY = "DyFM-CRY-IKY",
|
|
9
|
+
INVALID_DATA = "DyFM-CRY-IDT"
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for encryption/decryption
|
|
13
|
+
*/
|
|
14
|
+
export interface CryptoConfig {
|
|
15
|
+
ivLength?: number;
|
|
16
|
+
saltLength?: number;
|
|
17
|
+
keyIterations?: number;
|
|
18
|
+
keySize?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A utility class for secure encryption and decryption of data
|
|
22
|
+
* Uses AES-256-CBC with PBKDF2 key derivation
|
|
23
|
+
*/
|
|
24
|
+
export declare class DyFM_Crypto {
|
|
25
|
+
private static readonly DEFAULT_CONFIG;
|
|
26
|
+
/**
|
|
27
|
+
* Validates the input data and key
|
|
28
|
+
* @throws {DyFM_Error} if validation fails
|
|
29
|
+
*/
|
|
30
|
+
private static validateInput;
|
|
31
|
+
/**
|
|
32
|
+
* Generates a secure random padding
|
|
33
|
+
* @param length Length of the padding in bytes
|
|
34
|
+
*/
|
|
35
|
+
private static generatePadding;
|
|
36
|
+
/**
|
|
37
|
+
* Derives a key using PBKDF2
|
|
38
|
+
* @param key The input key
|
|
39
|
+
* @param salt The salt to use
|
|
40
|
+
* @param config Configuration options
|
|
41
|
+
*/
|
|
42
|
+
private static deriveKey;
|
|
43
|
+
/**
|
|
44
|
+
* Safely serializes data to JSON
|
|
45
|
+
* @param data The data to serialize
|
|
46
|
+
* @returns Serialized data as string
|
|
47
|
+
*/
|
|
48
|
+
private static safeSerialize;
|
|
49
|
+
/**
|
|
50
|
+
* Safely deserializes JSON data
|
|
51
|
+
* @param data The JSON string to deserialize
|
|
52
|
+
* @returns Deserialized data
|
|
53
|
+
*/
|
|
54
|
+
private static safeDeserialize;
|
|
55
|
+
/**
|
|
56
|
+
* Encrypts data using AES-256-CBC
|
|
57
|
+
* @param data The data to encrypt
|
|
58
|
+
* @param key The encryption key
|
|
59
|
+
* @param config Optional configuration
|
|
60
|
+
* @returns URL-safe encrypted string
|
|
61
|
+
* @throws {DyFM_Error} if encryption fails
|
|
62
|
+
*/
|
|
63
|
+
static encrypt<T>(data: T, key: string, config?: CryptoConfig): string;
|
|
64
|
+
/**
|
|
65
|
+
* Decrypts data that was encrypted using encrypt()
|
|
66
|
+
* @param encryptedData The encrypted data
|
|
67
|
+
* @param key The decryption key
|
|
68
|
+
* @param config Optional configuration
|
|
69
|
+
* @returns The decrypted data
|
|
70
|
+
* @throws {DyFM_Error} if decryption fails
|
|
71
|
+
*/
|
|
72
|
+
static decrypt<T>(encryptedData: string, key: string, config?: CryptoConfig): T;
|
|
73
|
+
/**
|
|
74
|
+
* Generates a secure random key
|
|
75
|
+
* @param length Length of the key in bytes (default: 32)
|
|
76
|
+
* @returns A secure random key
|
|
77
|
+
*/
|
|
78
|
+
static generateKey(length?: number): string;
|
|
79
|
+
/**
|
|
80
|
+
* Validates if a string is a valid encrypted data
|
|
81
|
+
* @param encryptedData The data to validate
|
|
82
|
+
* @returns true if the data appears to be valid encrypted data
|
|
83
|
+
*/
|
|
84
|
+
static isValidEncryptedData(encryptedData: string): boolean;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=crypto.util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/crypto/_collections/crypto.util.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,oBAAY,eAAe;IACzB,aAAa,iBAAiB;IAC9B,iBAAiB,iBAAiB;IAClC,iBAAiB,iBAAiB;IAClC,WAAW,iBAAiB;IAC5B,YAAY,iBAAiB;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAKpC;IAMF;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAkB5B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAI9B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS;IAOxB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAY5B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAY9B;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM;IAyCtE;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,CAAC;IAoD/E;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM;IAI/C;;;;OAIG;IACH,MAAM,CAAC,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;CAM5D"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyFM_Crypto = exports.CryptoErrorCode = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const CryptoJS = tslib_1.__importStar(require("crypto-js"));
|
|
6
|
+
const error_control_model_1 = require("../../../_models/control-models/error.control-model");
|
|
7
|
+
/**
|
|
8
|
+
* Error codes for crypto operations
|
|
9
|
+
*/
|
|
10
|
+
var CryptoErrorCode;
|
|
11
|
+
(function (CryptoErrorCode) {
|
|
12
|
+
CryptoErrorCode["INVALID_INPUT"] = "DyFM-CRY-EA0";
|
|
13
|
+
CryptoErrorCode["DECRYPTION_FAILED"] = "DyFM-CRY-DRY";
|
|
14
|
+
CryptoErrorCode["ENCRYPTION_FAILED"] = "DyFM-CRY-ENF";
|
|
15
|
+
CryptoErrorCode["INVALID_KEY"] = "DyFM-CRY-IKY";
|
|
16
|
+
CryptoErrorCode["INVALID_DATA"] = "DyFM-CRY-IDT";
|
|
17
|
+
})(CryptoErrorCode || (exports.CryptoErrorCode = CryptoErrorCode = {}));
|
|
18
|
+
/**
|
|
19
|
+
* A utility class for secure encryption and decryption of data
|
|
20
|
+
* Uses AES-256-CBC with PBKDF2 key derivation
|
|
21
|
+
*/
|
|
22
|
+
class DyFM_Crypto {
|
|
23
|
+
static DEFAULT_CONFIG = {
|
|
24
|
+
ivLength: 16,
|
|
25
|
+
saltLength: 16,
|
|
26
|
+
keyIterations: 10000,
|
|
27
|
+
keySize: 256 / 32
|
|
28
|
+
};
|
|
29
|
+
// Tömör: kb. 60–80 karakteres token, nem 200+
|
|
30
|
+
// Nem szabványos: nehéz visszafejteni
|
|
31
|
+
// Használható cookie, header, URL-ben
|
|
32
|
+
/**
|
|
33
|
+
* Validates the input data and key
|
|
34
|
+
* @throws {DyFM_Error} if validation fails
|
|
35
|
+
*/
|
|
36
|
+
static validateInput(data, key) {
|
|
37
|
+
if (data === undefined || data === null) {
|
|
38
|
+
throw new error_control_model_1.DyFM_Error({
|
|
39
|
+
message: 'Data is required',
|
|
40
|
+
error: new Error('Data is required'),
|
|
41
|
+
errorCode: CryptoErrorCode.INVALID_DATA
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
45
|
+
throw new error_control_model_1.DyFM_Error({
|
|
46
|
+
message: 'Valid key is required',
|
|
47
|
+
error: new Error('Key must be a non-empty string'),
|
|
48
|
+
errorCode: CryptoErrorCode.INVALID_KEY
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generates a secure random padding
|
|
54
|
+
* @param length Length of the padding in bytes
|
|
55
|
+
*/
|
|
56
|
+
static generatePadding(length) {
|
|
57
|
+
return CryptoJS.lib.WordArray.random(length).toString();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Derives a key using PBKDF2
|
|
61
|
+
* @param key The input key
|
|
62
|
+
* @param salt The salt to use
|
|
63
|
+
* @param config Configuration options
|
|
64
|
+
*/
|
|
65
|
+
static deriveKey(key, salt, config) {
|
|
66
|
+
return CryptoJS.PBKDF2(key, salt, {
|
|
67
|
+
keySize: config.keySize,
|
|
68
|
+
iterations: config.keyIterations
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Safely serializes data to JSON
|
|
73
|
+
* @param data The data to serialize
|
|
74
|
+
* @returns Serialized data as string
|
|
75
|
+
*/
|
|
76
|
+
static safeSerialize(data) {
|
|
77
|
+
try {
|
|
78
|
+
return JSON.stringify(data);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw new error_control_model_1.DyFM_Error({
|
|
82
|
+
message: 'Failed to serialize data',
|
|
83
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
84
|
+
errorCode: CryptoErrorCode.INVALID_DATA
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Safely deserializes JSON data
|
|
90
|
+
* @param data The JSON string to deserialize
|
|
91
|
+
* @returns Deserialized data
|
|
92
|
+
*/
|
|
93
|
+
static safeDeserialize(data) {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(data);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new error_control_model_1.DyFM_Error({
|
|
99
|
+
message: 'Failed to deserialize data',
|
|
100
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
101
|
+
errorCode: CryptoErrorCode.DECRYPTION_FAILED
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Encrypts data using AES-256-CBC
|
|
107
|
+
* @param data The data to encrypt
|
|
108
|
+
* @param key The encryption key
|
|
109
|
+
* @param config Optional configuration
|
|
110
|
+
* @returns URL-safe encrypted string
|
|
111
|
+
* @throws {DyFM_Error} if encryption fails
|
|
112
|
+
*/
|
|
113
|
+
static encrypt(data, key, config) {
|
|
114
|
+
try {
|
|
115
|
+
this.validateInput(data, key);
|
|
116
|
+
const finalConfig = { ...this.DEFAULT_CONFIG, ...config };
|
|
117
|
+
// Generate random IV and salt
|
|
118
|
+
const iv = CryptoJS.lib.WordArray.random(finalConfig.ivLength);
|
|
119
|
+
const salt = CryptoJS.lib.WordArray.random(finalConfig.saltLength);
|
|
120
|
+
// Derive key using PBKDF2
|
|
121
|
+
const derivedKey = this.deriveKey(key, salt, finalConfig);
|
|
122
|
+
// Convert data to string and add random padding
|
|
123
|
+
const dataStr = this.safeSerialize(data);
|
|
124
|
+
const padding = this.generatePadding(16);
|
|
125
|
+
const paddedData = padding + dataStr;
|
|
126
|
+
// Encrypt the data
|
|
127
|
+
const encrypted = CryptoJS.AES.encrypt(paddedData, derivedKey, {
|
|
128
|
+
iv: iv,
|
|
129
|
+
mode: CryptoJS.mode.CBC,
|
|
130
|
+
padding: CryptoJS.pad.Pkcs7
|
|
131
|
+
});
|
|
132
|
+
// Combine IV + Salt + Ciphertext
|
|
133
|
+
const combined = iv.concat(salt).concat(encrypted.ciphertext);
|
|
134
|
+
// Convert to URL-safe base64
|
|
135
|
+
return CryptoJS.enc.Base64.stringify(combined)
|
|
136
|
+
.replace(/\+/g, '-')
|
|
137
|
+
.replace(/\//g, '_')
|
|
138
|
+
.replace(/=+$/, '');
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
throw new error_control_model_1.DyFM_Error({
|
|
142
|
+
message: 'Error encrypting data',
|
|
143
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
144
|
+
errorCode: CryptoErrorCode.ENCRYPTION_FAILED
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Decrypts data that was encrypted using encrypt()
|
|
150
|
+
* @param encryptedData The encrypted data
|
|
151
|
+
* @param key The decryption key
|
|
152
|
+
* @param config Optional configuration
|
|
153
|
+
* @returns The decrypted data
|
|
154
|
+
* @throws {DyFM_Error} if decryption fails
|
|
155
|
+
*/
|
|
156
|
+
static decrypt(encryptedData, key, config) {
|
|
157
|
+
try {
|
|
158
|
+
this.validateInput(encryptedData, key);
|
|
159
|
+
const finalConfig = { ...this.DEFAULT_CONFIG, ...config };
|
|
160
|
+
// Convert from URL-safe base64
|
|
161
|
+
const base64 = encryptedData
|
|
162
|
+
.replace(/-/g, '+')
|
|
163
|
+
.replace(/_/g, '/');
|
|
164
|
+
// Parse the combined data
|
|
165
|
+
const combined = CryptoJS.enc.Base64.parse(base64);
|
|
166
|
+
// Extract IV, salt, and ciphertext
|
|
167
|
+
const iv = CryptoJS.lib.WordArray.create(combined.words.slice(0, finalConfig.ivLength / 4));
|
|
168
|
+
const salt = CryptoJS.lib.WordArray.create(combined.words.slice(finalConfig.ivLength / 4, (finalConfig.ivLength + finalConfig.saltLength) / 4));
|
|
169
|
+
const ciphertext = CryptoJS.lib.WordArray.create(combined.words.slice((finalConfig.ivLength + finalConfig.saltLength) / 4));
|
|
170
|
+
// Derive key using PBKDF2
|
|
171
|
+
const derivedKey = this.deriveKey(key, salt, finalConfig);
|
|
172
|
+
// Decrypt the data
|
|
173
|
+
const decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertext }, derivedKey, {
|
|
174
|
+
iv: iv,
|
|
175
|
+
mode: CryptoJS.mode.CBC,
|
|
176
|
+
padding: CryptoJS.pad.Pkcs7
|
|
177
|
+
});
|
|
178
|
+
// Remove padding and parse JSON
|
|
179
|
+
const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
|
180
|
+
const jsonStr = decryptedStr.slice(32); // Remove 16-byte padding
|
|
181
|
+
return this.safeDeserialize(jsonStr);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw new error_control_model_1.DyFM_Error({
|
|
185
|
+
message: 'Error decrypting data',
|
|
186
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
187
|
+
errorCode: CryptoErrorCode.DECRYPTION_FAILED
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Generates a secure random key
|
|
193
|
+
* @param length Length of the key in bytes (default: 32)
|
|
194
|
+
* @returns A secure random key
|
|
195
|
+
*/
|
|
196
|
+
static generateKey(length = 32) {
|
|
197
|
+
return CryptoJS.lib.WordArray.random(length).toString();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Validates if a string is a valid encrypted data
|
|
201
|
+
* @param encryptedData The data to validate
|
|
202
|
+
* @returns true if the data appears to be valid encrypted data
|
|
203
|
+
*/
|
|
204
|
+
static isValidEncryptedData(encryptedData) {
|
|
205
|
+
if (!encryptedData || typeof encryptedData !== 'string') {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return /^[A-Za-z0-9\-_]+$/.test(encryptedData);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.DyFM_Crypto = DyFM_Crypto;
|
|
212
|
+
//# sourceMappingURL=crypto.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.util.js","sourceRoot":"","sources":["../../../../src/_modules/crypto/_collections/crypto.util.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.util.spec.d.ts","sourceRoot":"","sources":["../../../../src/_modules/crypto/_collections/crypto.util.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto_util_1 = require("./crypto.util");
|
|
4
|
+
describe('DyFM_Crypto', () => {
|
|
5
|
+
const testKey = 'test-secret-key-123';
|
|
6
|
+
const testData = { id: 1, name: 'test' };
|
|
7
|
+
describe('encrypt', () => {
|
|
8
|
+
it('should successfully encrypt data', () => {
|
|
9
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
10
|
+
expect(encrypted).toBeDefined();
|
|
11
|
+
expect(typeof encrypted).toBe('string');
|
|
12
|
+
expect(encrypted).toMatch(/^[A-Za-z0-9\-_]+$/);
|
|
13
|
+
});
|
|
14
|
+
it('should encrypt different data with different results', () => {
|
|
15
|
+
const data1 = { id: 1 };
|
|
16
|
+
const data2 = { id: 2 };
|
|
17
|
+
const encrypted1 = crypto_util_1.DyFM_Crypto.encrypt(data1, testKey);
|
|
18
|
+
const encrypted2 = crypto_util_1.DyFM_Crypto.encrypt(data2, testKey);
|
|
19
|
+
expect(encrypted1).not.toEqual(encrypted2);
|
|
20
|
+
});
|
|
21
|
+
it('should throw DyFM_Error when encryption fails', () => {
|
|
22
|
+
// @ts-ignore - Testing invalid input
|
|
23
|
+
expect(() => crypto_util_1.DyFM_Crypto.encrypt(undefined, testKey)).toThrow();
|
|
24
|
+
});
|
|
25
|
+
it('should handle complex objects', () => {
|
|
26
|
+
const complexData = {
|
|
27
|
+
id: 1,
|
|
28
|
+
name: 'test',
|
|
29
|
+
nested: {
|
|
30
|
+
value: 'nested-value',
|
|
31
|
+
array: [1, 2, 3]
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(complexData, testKey);
|
|
35
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
36
|
+
expect(decrypted).toEqual(complexData);
|
|
37
|
+
});
|
|
38
|
+
it('should handle arrays', () => {
|
|
39
|
+
const arrayData = [1, 'test', { nested: true }];
|
|
40
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(arrayData, testKey);
|
|
41
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
42
|
+
expect(decrypted).toEqual(arrayData);
|
|
43
|
+
});
|
|
44
|
+
it('should handle primitive values', () => {
|
|
45
|
+
const stringData = 'test string';
|
|
46
|
+
const numberData = 42;
|
|
47
|
+
const booleanData = true;
|
|
48
|
+
const encryptedString = crypto_util_1.DyFM_Crypto.encrypt(stringData, testKey);
|
|
49
|
+
const encryptedNumber = crypto_util_1.DyFM_Crypto.encrypt(numberData, testKey);
|
|
50
|
+
const encryptedBoolean = crypto_util_1.DyFM_Crypto.encrypt(booleanData, testKey);
|
|
51
|
+
expect(crypto_util_1.DyFM_Crypto.decrypt(encryptedString, testKey)).toBe(stringData);
|
|
52
|
+
expect(crypto_util_1.DyFM_Crypto.decrypt(encryptedNumber, testKey)).toBe(numberData);
|
|
53
|
+
expect(crypto_util_1.DyFM_Crypto.decrypt(encryptedBoolean, testKey)).toBe(booleanData);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('decrypt', () => {
|
|
57
|
+
it('should successfully decrypt encrypted data', () => {
|
|
58
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
59
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
60
|
+
expect(decrypted).toEqual(testData);
|
|
61
|
+
});
|
|
62
|
+
it('should throw DyFM_Error when decryption fails', () => {
|
|
63
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt('invalid-encrypted-data', testKey)).toThrow();
|
|
64
|
+
});
|
|
65
|
+
it('should throw DyFM_Error when using wrong key', () => {
|
|
66
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
67
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt(encrypted, 'wrong-key')).toThrow();
|
|
68
|
+
});
|
|
69
|
+
it('should throw DyFM_Error when encrypted data is modified', () => {
|
|
70
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
71
|
+
const modified = encrypted.slice(0, -1) + 'A';
|
|
72
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt(modified, testKey)).toThrow();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('URL-safe encryption', () => {
|
|
76
|
+
it('should produce URL-safe strings', () => {
|
|
77
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
78
|
+
expect(encrypted).toMatch(/^[A-Za-z0-9\-_]+$/);
|
|
79
|
+
});
|
|
80
|
+
it('should work in HTTP headers', () => {
|
|
81
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
82
|
+
const header = `Bearer ${encrypted}`;
|
|
83
|
+
const extracted = header.split(' ')[1];
|
|
84
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(extracted, testKey);
|
|
85
|
+
expect(decrypted).toEqual(testData);
|
|
86
|
+
});
|
|
87
|
+
it('should work in URLs', () => {
|
|
88
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
89
|
+
const url = `https://example.com/api?token=${encrypted}`;
|
|
90
|
+
const params = new URLSearchParams(url.split('?')[1]);
|
|
91
|
+
const extracted = params.get('token');
|
|
92
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(extracted, testKey);
|
|
93
|
+
expect(decrypted).toEqual(testData);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('edge cases', () => {
|
|
97
|
+
describe('empty and null values', () => {
|
|
98
|
+
it('should handle empty objects', () => {
|
|
99
|
+
const emptyObj = {};
|
|
100
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(emptyObj, testKey);
|
|
101
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
102
|
+
expect(decrypted).toEqual(emptyObj);
|
|
103
|
+
});
|
|
104
|
+
it('should handle empty arrays', () => {
|
|
105
|
+
const emptyArr = [];
|
|
106
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(emptyArr, testKey);
|
|
107
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
108
|
+
expect(decrypted).toEqual(emptyArr);
|
|
109
|
+
});
|
|
110
|
+
it('should handle null values in objects', () => {
|
|
111
|
+
const dataWithNull = {
|
|
112
|
+
id: 1,
|
|
113
|
+
name: null,
|
|
114
|
+
nested: { value: null }
|
|
115
|
+
};
|
|
116
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(dataWithNull, testKey);
|
|
117
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
118
|
+
expect(decrypted).toEqual(dataWithNull);
|
|
119
|
+
});
|
|
120
|
+
it('should handle undefined values in objects', () => {
|
|
121
|
+
const dataWithUndefined = {
|
|
122
|
+
id: 1,
|
|
123
|
+
name: undefined,
|
|
124
|
+
nested: { value: undefined }
|
|
125
|
+
};
|
|
126
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(dataWithUndefined, testKey);
|
|
127
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, testKey);
|
|
128
|
+
expect(decrypted).toEqual({ id: 1, nested: {} });
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('key edge cases', () => {
|
|
132
|
+
it('should handle very long keys', () => {
|
|
133
|
+
const longKey = 'a'.repeat(1000);
|
|
134
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, longKey);
|
|
135
|
+
const decrypted = crypto_util_1.DyFM_Crypto.decrypt(encrypted, longKey);
|
|
136
|
+
expect(decrypted).toEqual(testData);
|
|
137
|
+
});
|
|
138
|
+
it('should handle empty key', () => {
|
|
139
|
+
expect(() => crypto_util_1.DyFM_Crypto.encrypt(testData, '')).toThrow();
|
|
140
|
+
});
|
|
141
|
+
it('should handle whitespace-only keys', () => {
|
|
142
|
+
expect(() => crypto_util_1.DyFM_Crypto.encrypt(testData, ' ')).toThrow();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('encrypted data manipulation', () => {
|
|
146
|
+
it('should detect when encrypted data is truncated', () => {
|
|
147
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
148
|
+
const truncated = encrypted.slice(0, -10);
|
|
149
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt(truncated, testKey)).toThrow();
|
|
150
|
+
});
|
|
151
|
+
it('should detect when encrypted data is extended', () => {
|
|
152
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
153
|
+
const extended = encrypted + 'A'.repeat(10);
|
|
154
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt(extended, testKey)).toThrow();
|
|
155
|
+
});
|
|
156
|
+
it('should detect when encrypted data is modified in the middle', () => {
|
|
157
|
+
const encrypted = crypto_util_1.DyFM_Crypto.encrypt(testData, testKey);
|
|
158
|
+
const midPoint = Math.floor(encrypted.length / 2);
|
|
159
|
+
const modified = encrypted.slice(0, midPoint) + 'X' + encrypted.slice(midPoint + 1);
|
|
160
|
+
expect(() => crypto_util_1.DyFM_Crypto.decrypt(modified, testKey)).toThrow();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=crypto.util.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.util.spec.js","sourceRoot":"","sources":["../../../../src/_modules/crypto/_collections/crypto.util.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/crypto/index.ts"],"names":[],"mappings":"AAEA,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/crypto/index.ts"],"names":[],"mappings":";;;AAEA,qEAA2C"}
|
|
Binary file
|
package/nodemon.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fsm-dynamo",
|
|
3
|
-
"version": "01.10.
|
|
3
|
+
"version": "01.10.33",
|
|
4
4
|
"description": "Full Stack Model Collection for Dynamic (NodeJS-Typescript) Framework called Dynamo, by Future Development Ltd.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prep": "npm i pnpm -g && npm i rimraf nodemon -g",
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"build-base": "pnpm i && rimraf ./build && npx tsc",
|
|
16
16
|
"build-clean": "pnpm run clean && pnpm run build-base",
|
|
17
17
|
"build-simple": "pnpm i && npx tsc && jasmine",
|
|
18
|
+
"build-n-test": "npm run build-tgz",
|
|
19
|
+
|
|
20
|
+
"nodemon-run": "npm run build-tgz",
|
|
18
21
|
|
|
19
22
|
"clean": "pnpm run prep && rimraf ./build && rimraf package-lock.json && rimraf pnpm-lock.yaml && rimraf ./node_modules",
|
|
20
23
|
"test": "pnpm run build-base && jasmine",
|
|
@@ -72,6 +75,12 @@
|
|
|
72
75
|
"module": "./build/_modules/usage/index.js",
|
|
73
76
|
"types": "./build/_modules/usage/index.d.ts",
|
|
74
77
|
"typings": "./build/_modules/usage/index.d.ts"
|
|
78
|
+
},
|
|
79
|
+
"./crypto": {
|
|
80
|
+
"default": "./build/_modules/crypto/index.js",
|
|
81
|
+
"module": "./build/_modules/crypto/index.js",
|
|
82
|
+
"types": "./build/_modules/crypto/index.d.ts",
|
|
83
|
+
"typings": "./build/_modules/crypto/index.d.ts"
|
|
75
84
|
}
|
|
76
85
|
},
|
|
77
86
|
"typesVersions": {
|
|
@@ -82,7 +91,8 @@
|
|
|
82
91
|
"pipe": [ "build/_modules/pipe/index.d.ts" ],
|
|
83
92
|
"socket": [ "build/_modules/socket/index.d.ts" ],
|
|
84
93
|
"test": [ "build/_modules/test/index.d.ts" ],
|
|
85
|
-
"usage": [ "build/_modules/usage/index.d.ts" ]
|
|
94
|
+
"usage": [ "build/_modules/usage/index.d.ts" ],
|
|
95
|
+
"crypto": [ "build/_modules/crypto/index.d.ts" ]
|
|
86
96
|
}
|
|
87
97
|
},
|
|
88
98
|
"keywords": [],
|
|
@@ -110,6 +120,7 @@
|
|
|
110
120
|
"eslint-plugin-unused-imports": "^4.1.4",
|
|
111
121
|
"jasmine": "~5.1.0",
|
|
112
122
|
"jasmine-spec-reporter": "^7.0.0",
|
|
123
|
+
"crypto-js": "~4.2.0",
|
|
113
124
|
"openai": "^4.93.0"
|
|
114
125
|
}
|
|
115
126
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { DyFM_Crypto } from './crypto.util';
|
|
2
|
+
|
|
3
|
+
describe('DyFM_Crypto', () => {
|
|
4
|
+
const testKey = 'test-secret-key-123';
|
|
5
|
+
const testData = { id: 1, name: 'test' };
|
|
6
|
+
|
|
7
|
+
describe('encrypt', () => {
|
|
8
|
+
it('should successfully encrypt data', () => {
|
|
9
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
10
|
+
|
|
11
|
+
expect(encrypted).toBeDefined();
|
|
12
|
+
expect(typeof encrypted).toBe('string');
|
|
13
|
+
expect(encrypted).toMatch(/^[A-Za-z0-9\-_]+$/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should encrypt different data with different results', () => {
|
|
17
|
+
const data1 = { id: 1 };
|
|
18
|
+
const data2 = { id: 2 };
|
|
19
|
+
|
|
20
|
+
const encrypted1 = DyFM_Crypto.encrypt(data1, testKey);
|
|
21
|
+
const encrypted2 = DyFM_Crypto.encrypt(data2, testKey);
|
|
22
|
+
|
|
23
|
+
expect(encrypted1).not.toEqual(encrypted2);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should throw DyFM_Error when encryption fails', () => {
|
|
27
|
+
// @ts-ignore - Testing invalid input
|
|
28
|
+
expect(() => DyFM_Crypto.encrypt(undefined, testKey)).toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should handle complex objects', () => {
|
|
32
|
+
const complexData = {
|
|
33
|
+
id: 1,
|
|
34
|
+
name: 'test',
|
|
35
|
+
nested: {
|
|
36
|
+
value: 'nested-value',
|
|
37
|
+
array: [1, 2, 3]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const encrypted = DyFM_Crypto.encrypt(complexData, testKey);
|
|
42
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
43
|
+
|
|
44
|
+
expect(decrypted).toEqual(complexData);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle arrays', () => {
|
|
48
|
+
const arrayData = [1, 'test', { nested: true }];
|
|
49
|
+
const encrypted = DyFM_Crypto.encrypt(arrayData, testKey);
|
|
50
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
51
|
+
|
|
52
|
+
expect(decrypted).toEqual(arrayData);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle primitive values', () => {
|
|
56
|
+
const stringData = 'test string';
|
|
57
|
+
const numberData = 42;
|
|
58
|
+
const booleanData = true;
|
|
59
|
+
|
|
60
|
+
const encryptedString = DyFM_Crypto.encrypt(stringData, testKey);
|
|
61
|
+
const encryptedNumber = DyFM_Crypto.encrypt(numberData, testKey);
|
|
62
|
+
const encryptedBoolean = DyFM_Crypto.encrypt(booleanData, testKey);
|
|
63
|
+
|
|
64
|
+
expect(DyFM_Crypto.decrypt(encryptedString, testKey)).toBe(stringData);
|
|
65
|
+
expect(DyFM_Crypto.decrypt(encryptedNumber, testKey)).toBe(numberData);
|
|
66
|
+
expect(DyFM_Crypto.decrypt(encryptedBoolean, testKey)).toBe(booleanData);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('decrypt', () => {
|
|
71
|
+
it('should successfully decrypt encrypted data', () => {
|
|
72
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
73
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
74
|
+
|
|
75
|
+
expect(decrypted).toEqual(testData);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should throw DyFM_Error when decryption fails', () => {
|
|
79
|
+
expect(() => DyFM_Crypto.decrypt('invalid-encrypted-data', testKey)).toThrow();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should throw DyFM_Error when using wrong key', () => {
|
|
83
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
84
|
+
expect(() => DyFM_Crypto.decrypt(encrypted, 'wrong-key')).toThrow();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should throw DyFM_Error when encrypted data is modified', () => {
|
|
88
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
89
|
+
const modified = encrypted.slice(0, -1) + 'A';
|
|
90
|
+
expect(() => DyFM_Crypto.decrypt(modified, testKey)).toThrow();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('URL-safe encryption', () => {
|
|
95
|
+
it('should produce URL-safe strings', () => {
|
|
96
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
97
|
+
expect(encrypted).toMatch(/^[A-Za-z0-9\-_]+$/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should work in HTTP headers', () => {
|
|
101
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
102
|
+
const header = `Bearer ${encrypted}`;
|
|
103
|
+
const extracted = header.split(' ')[1];
|
|
104
|
+
const decrypted = DyFM_Crypto.decrypt(extracted, testKey);
|
|
105
|
+
expect(decrypted).toEqual(testData);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should work in URLs', () => {
|
|
109
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
110
|
+
const url = `https://example.com/api?token=${encrypted}`;
|
|
111
|
+
const params = new URLSearchParams(url.split('?')[1]);
|
|
112
|
+
const extracted = params.get('token');
|
|
113
|
+
const decrypted = DyFM_Crypto.decrypt(extracted!, testKey);
|
|
114
|
+
expect(decrypted).toEqual(testData);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('edge cases', () => {
|
|
119
|
+
describe('empty and null values', () => {
|
|
120
|
+
it('should handle empty objects', () => {
|
|
121
|
+
const emptyObj = {};
|
|
122
|
+
const encrypted = DyFM_Crypto.encrypt(emptyObj, testKey);
|
|
123
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
124
|
+
expect(decrypted).toEqual(emptyObj);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle empty arrays', () => {
|
|
128
|
+
const emptyArr: any[] = [];
|
|
129
|
+
const encrypted = DyFM_Crypto.encrypt(emptyArr, testKey);
|
|
130
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
131
|
+
expect(decrypted).toEqual(emptyArr);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should handle null values in objects', () => {
|
|
135
|
+
const dataWithNull = {
|
|
136
|
+
id: 1,
|
|
137
|
+
name: null,
|
|
138
|
+
nested: { value: null }
|
|
139
|
+
};
|
|
140
|
+
const encrypted = DyFM_Crypto.encrypt(dataWithNull, testKey);
|
|
141
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
142
|
+
expect(decrypted).toEqual(dataWithNull);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle undefined values in objects', () => {
|
|
146
|
+
const dataWithUndefined = {
|
|
147
|
+
id: 1,
|
|
148
|
+
name: undefined,
|
|
149
|
+
nested: { value: undefined }
|
|
150
|
+
};
|
|
151
|
+
const encrypted = DyFM_Crypto.encrypt(dataWithUndefined, testKey);
|
|
152
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, testKey);
|
|
153
|
+
expect(decrypted).toEqual({ id: 1, nested: {} });
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('key edge cases', () => {
|
|
158
|
+
it('should handle very long keys', () => {
|
|
159
|
+
const longKey = 'a'.repeat(1000);
|
|
160
|
+
const encrypted = DyFM_Crypto.encrypt(testData, longKey);
|
|
161
|
+
const decrypted = DyFM_Crypto.decrypt(encrypted, longKey);
|
|
162
|
+
expect(decrypted).toEqual(testData);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle empty key', () => {
|
|
166
|
+
expect(() => DyFM_Crypto.encrypt(testData, '')).toThrow();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle whitespace-only keys', () => {
|
|
170
|
+
expect(() => DyFM_Crypto.encrypt(testData, ' ')).toThrow();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('encrypted data manipulation', () => {
|
|
175
|
+
it('should detect when encrypted data is truncated', () => {
|
|
176
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
177
|
+
const truncated = encrypted.slice(0, -10);
|
|
178
|
+
expect(() => DyFM_Crypto.decrypt(truncated, testKey)).toThrow();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should detect when encrypted data is extended', () => {
|
|
182
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
183
|
+
const extended = encrypted + 'A'.repeat(10);
|
|
184
|
+
expect(() => DyFM_Crypto.decrypt(extended, testKey)).toThrow();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should detect when encrypted data is modified in the middle', () => {
|
|
188
|
+
const encrypted = DyFM_Crypto.encrypt(testData, testKey);
|
|
189
|
+
const midPoint = Math.floor(encrypted.length / 2);
|
|
190
|
+
const modified = encrypted.slice(0, midPoint) + 'X' + encrypted.slice(midPoint + 1);
|
|
191
|
+
expect(() => DyFM_Crypto.decrypt(modified, testKey)).toThrow();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import * as CryptoJS from 'crypto-js';
|
|
2
|
+
|
|
3
|
+
import { DyFM_Error } from '../../../_models/control-models/error.control-model';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Error codes for crypto operations
|
|
7
|
+
*/
|
|
8
|
+
export enum CryptoErrorCode {
|
|
9
|
+
INVALID_INPUT = 'DyFM-CRY-EA0',
|
|
10
|
+
DECRYPTION_FAILED = 'DyFM-CRY-DRY',
|
|
11
|
+
ENCRYPTION_FAILED = 'DyFM-CRY-ENF',
|
|
12
|
+
INVALID_KEY = 'DyFM-CRY-IKY',
|
|
13
|
+
INVALID_DATA = 'DyFM-CRY-IDT'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Configuration options for encryption/decryption
|
|
18
|
+
*/
|
|
19
|
+
export interface CryptoConfig {
|
|
20
|
+
ivLength?: number;
|
|
21
|
+
saltLength?: number;
|
|
22
|
+
keyIterations?: number;
|
|
23
|
+
keySize?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A utility class for secure encryption and decryption of data
|
|
28
|
+
* Uses AES-256-CBC with PBKDF2 key derivation
|
|
29
|
+
*/
|
|
30
|
+
export class DyFM_Crypto {
|
|
31
|
+
private static readonly DEFAULT_CONFIG: Required<CryptoConfig> = {
|
|
32
|
+
ivLength: 16,
|
|
33
|
+
saltLength: 16,
|
|
34
|
+
keyIterations: 10000,
|
|
35
|
+
keySize: 256 / 32
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Tömör: kb. 60–80 karakteres token, nem 200+
|
|
39
|
+
// Nem szabványos: nehéz visszafejteni
|
|
40
|
+
// Használható cookie, header, URL-ben
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validates the input data and key
|
|
44
|
+
* @throws {DyFM_Error} if validation fails
|
|
45
|
+
*/
|
|
46
|
+
private static validateInput<T>(data: T, key: string): void {
|
|
47
|
+
if (data === undefined || data === null) {
|
|
48
|
+
throw new DyFM_Error({
|
|
49
|
+
message: 'Data is required',
|
|
50
|
+
error: new Error('Data is required'),
|
|
51
|
+
errorCode: CryptoErrorCode.INVALID_DATA
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
56
|
+
throw new DyFM_Error({
|
|
57
|
+
message: 'Valid key is required',
|
|
58
|
+
error: new Error('Key must be a non-empty string'),
|
|
59
|
+
errorCode: CryptoErrorCode.INVALID_KEY
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generates a secure random padding
|
|
66
|
+
* @param length Length of the padding in bytes
|
|
67
|
+
*/
|
|
68
|
+
private static generatePadding(length: number): string {
|
|
69
|
+
return CryptoJS.lib.WordArray.random(length).toString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Derives a key using PBKDF2
|
|
74
|
+
* @param key The input key
|
|
75
|
+
* @param salt The salt to use
|
|
76
|
+
* @param config Configuration options
|
|
77
|
+
*/
|
|
78
|
+
private static deriveKey(key: string, salt: CryptoJS.lib.WordArray, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
|
|
79
|
+
return CryptoJS.PBKDF2(key, salt, {
|
|
80
|
+
keySize: config.keySize,
|
|
81
|
+
iterations: config.keyIterations
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Safely serializes data to JSON
|
|
87
|
+
* @param data The data to serialize
|
|
88
|
+
* @returns Serialized data as string
|
|
89
|
+
*/
|
|
90
|
+
private static safeSerialize<T>(data: T): string {
|
|
91
|
+
try {
|
|
92
|
+
return JSON.stringify(data);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new DyFM_Error({
|
|
95
|
+
message: 'Failed to serialize data',
|
|
96
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
97
|
+
errorCode: CryptoErrorCode.INVALID_DATA
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Safely deserializes JSON data
|
|
104
|
+
* @param data The JSON string to deserialize
|
|
105
|
+
* @returns Deserialized data
|
|
106
|
+
*/
|
|
107
|
+
private static safeDeserialize<T>(data: string): T {
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(data);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw new DyFM_Error({
|
|
112
|
+
message: 'Failed to deserialize data',
|
|
113
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
114
|
+
errorCode: CryptoErrorCode.DECRYPTION_FAILED
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Encrypts data using AES-256-CBC
|
|
121
|
+
* @param data The data to encrypt
|
|
122
|
+
* @param key The encryption key
|
|
123
|
+
* @param config Optional configuration
|
|
124
|
+
* @returns URL-safe encrypted string
|
|
125
|
+
* @throws {DyFM_Error} if encryption fails
|
|
126
|
+
*/
|
|
127
|
+
static encrypt<T>(data: T, key: string, config?: CryptoConfig): string {
|
|
128
|
+
try {
|
|
129
|
+
this.validateInput(data, key);
|
|
130
|
+
const finalConfig = { ...this.DEFAULT_CONFIG, ...config };
|
|
131
|
+
|
|
132
|
+
// Generate random IV and salt
|
|
133
|
+
const iv = CryptoJS.lib.WordArray.random(finalConfig.ivLength);
|
|
134
|
+
const salt = CryptoJS.lib.WordArray.random(finalConfig.saltLength);
|
|
135
|
+
|
|
136
|
+
// Derive key using PBKDF2
|
|
137
|
+
const derivedKey = this.deriveKey(key, salt, finalConfig);
|
|
138
|
+
|
|
139
|
+
// Convert data to string and add random padding
|
|
140
|
+
const dataStr = this.safeSerialize(data);
|
|
141
|
+
const padding = this.generatePadding(16);
|
|
142
|
+
const paddedData = padding + dataStr;
|
|
143
|
+
|
|
144
|
+
// Encrypt the data
|
|
145
|
+
const encrypted = CryptoJS.AES.encrypt(paddedData, derivedKey, {
|
|
146
|
+
iv: iv,
|
|
147
|
+
mode: CryptoJS.mode.CBC,
|
|
148
|
+
padding: CryptoJS.pad.Pkcs7
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Combine IV + Salt + Ciphertext
|
|
152
|
+
const combined = iv.concat(salt).concat(encrypted.ciphertext);
|
|
153
|
+
|
|
154
|
+
// Convert to URL-safe base64
|
|
155
|
+
return CryptoJS.enc.Base64.stringify(combined)
|
|
156
|
+
.replace(/\+/g, '-')
|
|
157
|
+
.replace(/\//g, '_')
|
|
158
|
+
.replace(/=+$/, '');
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new DyFM_Error({
|
|
161
|
+
message: 'Error encrypting data',
|
|
162
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
163
|
+
errorCode: CryptoErrorCode.ENCRYPTION_FAILED
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Decrypts data that was encrypted using encrypt()
|
|
170
|
+
* @param encryptedData The encrypted data
|
|
171
|
+
* @param key The decryption key
|
|
172
|
+
* @param config Optional configuration
|
|
173
|
+
* @returns The decrypted data
|
|
174
|
+
* @throws {DyFM_Error} if decryption fails
|
|
175
|
+
*/
|
|
176
|
+
static decrypt<T>(encryptedData: string, key: string, config?: CryptoConfig): T {
|
|
177
|
+
try {
|
|
178
|
+
this.validateInput(encryptedData, key);
|
|
179
|
+
const finalConfig = { ...this.DEFAULT_CONFIG, ...config };
|
|
180
|
+
|
|
181
|
+
// Convert from URL-safe base64
|
|
182
|
+
const base64 = encryptedData
|
|
183
|
+
.replace(/-/g, '+')
|
|
184
|
+
.replace(/_/g, '/');
|
|
185
|
+
|
|
186
|
+
// Parse the combined data
|
|
187
|
+
const combined = CryptoJS.enc.Base64.parse(base64);
|
|
188
|
+
|
|
189
|
+
// Extract IV, salt, and ciphertext
|
|
190
|
+
const iv = CryptoJS.lib.WordArray.create(combined.words.slice(0, finalConfig.ivLength / 4));
|
|
191
|
+
const salt = CryptoJS.lib.WordArray.create(
|
|
192
|
+
combined.words.slice(
|
|
193
|
+
finalConfig.ivLength / 4,
|
|
194
|
+
(finalConfig.ivLength + finalConfig.saltLength) / 4
|
|
195
|
+
)
|
|
196
|
+
);
|
|
197
|
+
const ciphertext = CryptoJS.lib.WordArray.create(
|
|
198
|
+
combined.words.slice((finalConfig.ivLength + finalConfig.saltLength) / 4)
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Derive key using PBKDF2
|
|
202
|
+
const derivedKey = this.deriveKey(key, salt, finalConfig);
|
|
203
|
+
|
|
204
|
+
// Decrypt the data
|
|
205
|
+
const decrypted = CryptoJS.AES.decrypt(
|
|
206
|
+
{ ciphertext: ciphertext },
|
|
207
|
+
derivedKey,
|
|
208
|
+
{
|
|
209
|
+
iv: iv,
|
|
210
|
+
mode: CryptoJS.mode.CBC,
|
|
211
|
+
padding: CryptoJS.pad.Pkcs7
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Remove padding and parse JSON
|
|
216
|
+
const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
|
217
|
+
const jsonStr = decryptedStr.slice(32); // Remove 16-byte padding
|
|
218
|
+
return this.safeDeserialize<T>(jsonStr);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
throw new DyFM_Error({
|
|
221
|
+
message: 'Error decrypting data',
|
|
222
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
223
|
+
errorCode: CryptoErrorCode.DECRYPTION_FAILED
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Generates a secure random key
|
|
230
|
+
* @param length Length of the key in bytes (default: 32)
|
|
231
|
+
* @returns A secure random key
|
|
232
|
+
*/
|
|
233
|
+
static generateKey(length: number = 32): string {
|
|
234
|
+
return CryptoJS.lib.WordArray.random(length).toString();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Validates if a string is a valid encrypted data
|
|
239
|
+
* @param encryptedData The data to validate
|
|
240
|
+
* @returns true if the data appears to be valid encrypted data
|
|
241
|
+
*/
|
|
242
|
+
static isValidEncryptedData(encryptedData: string): boolean {
|
|
243
|
+
if (!encryptedData || typeof encryptedData !== 'string') {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
return /^[A-Za-z0-9\-_]+$/.test(encryptedData);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
Binary file
|