@vvlad1973/crypto 2.1.1 → 2.3.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.
- package/dist/__tests__/crypto.test.js +44 -1
- package/dist/crypto.d.ts +36 -1
- package/dist/crypto.js +51 -1
- package/package.json +2 -2
- package/src/crypto.ts +55 -1
- package/.vscode/launch.json +0 -20
- package/CLAUDE.md +0 -90
- package/src/__tests__/crypto.test.ts +0 -238
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -36
|
@@ -35,7 +35,6 @@ describe('Crypto', () => {
|
|
|
35
35
|
expect(crypto).toBeDefined();
|
|
36
36
|
});
|
|
37
37
|
it('should return UUIDv4', () => {
|
|
38
|
-
const crypto = new Crypto(options);
|
|
39
38
|
const uuid = Crypto.getUUID();
|
|
40
39
|
const uuidv4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
41
40
|
expect(uuid).toMatch(uuidv4Regex);
|
|
@@ -153,6 +152,50 @@ describe('isCrypto', () => {
|
|
|
153
152
|
expect(isCrypto(plainObject)).toBe(false);
|
|
154
153
|
});
|
|
155
154
|
});
|
|
155
|
+
describe('encryptStorable / decryptStorable', () => {
|
|
156
|
+
const password = 'testPassword';
|
|
157
|
+
const salt = 'testSalt';
|
|
158
|
+
const plainText = 'Hello, World!';
|
|
159
|
+
it('round-trips correctly', () => {
|
|
160
|
+
const crypto = new Crypto(password, salt);
|
|
161
|
+
expect(crypto.decryptStorable(crypto.encryptStorable(plainText))).toBe(plainText);
|
|
162
|
+
});
|
|
163
|
+
it('produces different ciphertext on each call (fresh IV)', () => {
|
|
164
|
+
const crypto = new Crypto(password, salt);
|
|
165
|
+
expect(crypto.encryptStorable(plainText)).not.toBe(crypto.encryptStorable(plainText));
|
|
166
|
+
});
|
|
167
|
+
it('output is longer than encrypt() by exactly 16 bytes (32 hex chars)', () => {
|
|
168
|
+
const crypto = new Crypto(password, salt, 'sha512', 1000, 32, 0);
|
|
169
|
+
const storable = crypto.encryptStorable(plainText);
|
|
170
|
+
const legacy = crypto.encrypt(plainText);
|
|
171
|
+
expect(storable.length).toBe(legacy.length + 32);
|
|
172
|
+
});
|
|
173
|
+
it('two different instances with same key can cross-decrypt', () => {
|
|
174
|
+
const a = new Crypto(password, salt);
|
|
175
|
+
const b = new Crypto(password, salt);
|
|
176
|
+
expect(b.decryptStorable(a.encryptStorable(plainText))).toBe(plainText);
|
|
177
|
+
});
|
|
178
|
+
it('throws RangeError when input is too short', () => {
|
|
179
|
+
const crypto = new Crypto(password, salt);
|
|
180
|
+
expect(() => crypto.decryptStorable('aabbcc')).toThrow(RangeError);
|
|
181
|
+
});
|
|
182
|
+
it('handles empty string', () => {
|
|
183
|
+
const crypto = new Crypto(password, salt);
|
|
184
|
+
expect(crypto.decryptStorable(crypto.encryptStorable(''))).toBe('');
|
|
185
|
+
});
|
|
186
|
+
it('handles Unicode text', () => {
|
|
187
|
+
const crypto = new Crypto(password, salt);
|
|
188
|
+
const unicode = '🔐 Шифрование 中文';
|
|
189
|
+
expect(crypto.decryptStorable(crypto.encryptStorable(unicode))).toBe(unicode);
|
|
190
|
+
});
|
|
191
|
+
it('construction-time IV does not affect result', () => {
|
|
192
|
+
const c0 = new Crypto(password, salt, 'sha512', 1000, 32, 0);
|
|
193
|
+
const c5 = new Crypto(password, salt, 'sha512', 1000, 32, 5);
|
|
194
|
+
// Both should decrypt successfully regardless of instance IV
|
|
195
|
+
expect(c5.decryptStorable(c0.encryptStorable(plainText))).toBe(plainText);
|
|
196
|
+
expect(c0.decryptStorable(c5.encryptStorable(plainText))).toBe(plainText);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
156
199
|
describe('Compatibility with previous version', () => {
|
|
157
200
|
const plainText = 'Проверка связи';
|
|
158
201
|
let options;
|
package/dist/crypto.d.ts
CHANGED
|
@@ -40,16 +40,51 @@ export declare class Crypto {
|
|
|
40
40
|
private convertNumberToIV;
|
|
41
41
|
/**
|
|
42
42
|
* Encrypt a text string.
|
|
43
|
+
* Uses the IV supplied at construction time (fixed or random-at-construct).
|
|
44
|
+
* The IV is **not** embedded in the output — both sides must use the same IV.
|
|
45
|
+
* For persistent storage use {@link encryptStorable} instead.
|
|
46
|
+
*
|
|
43
47
|
* @param {string} text - The plain text to encrypt.
|
|
44
48
|
* @returns {string} The encrypted text as a hex string.
|
|
45
49
|
*/
|
|
46
50
|
encrypt(text: string): string;
|
|
47
51
|
/**
|
|
48
|
-
* Decrypt an encrypted text string.
|
|
52
|
+
* Decrypt an encrypted text string produced by {@link encrypt}.
|
|
53
|
+
* Uses the IV supplied at construction time.
|
|
54
|
+
*
|
|
49
55
|
* @param {string} text - The encrypted text as a hex string.
|
|
50
56
|
* @returns {string} The decrypted plain text.
|
|
51
57
|
*/
|
|
52
58
|
decrypt(text: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Encrypts text and prepends a fresh random IV to the output.
|
|
61
|
+
*
|
|
62
|
+
* Use this method when the ciphertext will be stored (database, file, etc.)
|
|
63
|
+
* and retrieved later in a different process instance. A new random IV is
|
|
64
|
+
* generated for every call, so repeated encryptions of the same plaintext
|
|
65
|
+
* produce different ciphertexts — no IV reuse across records.
|
|
66
|
+
*
|
|
67
|
+
* Output format: `hex( IV[16 bytes] || ciphertext[N bytes] )`
|
|
68
|
+
*
|
|
69
|
+
* The construction-time IV is **not** used by this method.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} text - The plain text to encrypt.
|
|
72
|
+
* @returns {string} Hex string containing the prepended IV and ciphertext.
|
|
73
|
+
* @see {@link decryptStorable}
|
|
74
|
+
*/
|
|
75
|
+
encryptStorable(text: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Decrypts a value produced by {@link encryptStorable}.
|
|
78
|
+
*
|
|
79
|
+
* Extracts the first 16 bytes as the IV, then decrypts the remainder.
|
|
80
|
+
* The construction-time IV is **not** used by this method.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} text - Hex string `IV[16 bytes] || ciphertext` from {@link encryptStorable}.
|
|
83
|
+
* @returns {string} The decrypted plain text.
|
|
84
|
+
* @throws {RangeError} When the input is shorter than 16 bytes (32 hex chars).
|
|
85
|
+
* @see {@link encryptStorable}
|
|
86
|
+
*/
|
|
87
|
+
decryptStorable(text: string): string;
|
|
53
88
|
/**
|
|
54
89
|
* Returns a UUID ver.4 string.
|
|
55
90
|
* @returns {string} UUID ver.4 string.
|
package/dist/crypto.js
CHANGED
|
@@ -53,6 +53,10 @@ export class Crypto {
|
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Encrypt a text string.
|
|
56
|
+
* Uses the IV supplied at construction time (fixed or random-at-construct).
|
|
57
|
+
* The IV is **not** embedded in the output — both sides must use the same IV.
|
|
58
|
+
* For persistent storage use {@link encryptStorable} instead.
|
|
59
|
+
*
|
|
56
60
|
* @param {string} text - The plain text to encrypt.
|
|
57
61
|
* @returns {string} The encrypted text as a hex string.
|
|
58
62
|
*/
|
|
@@ -65,7 +69,9 @@ export class Crypto {
|
|
|
65
69
|
return encrypted.toString('hex');
|
|
66
70
|
}
|
|
67
71
|
/**
|
|
68
|
-
* Decrypt an encrypted text string.
|
|
72
|
+
* Decrypt an encrypted text string produced by {@link encrypt}.
|
|
73
|
+
* Uses the IV supplied at construction time.
|
|
74
|
+
*
|
|
69
75
|
* @param {string} text - The encrypted text as a hex string.
|
|
70
76
|
* @returns {string} The decrypted plain text.
|
|
71
77
|
*/
|
|
@@ -78,6 +84,50 @@ export class Crypto {
|
|
|
78
84
|
]);
|
|
79
85
|
return decrypted.toString('utf8');
|
|
80
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Encrypts text and prepends a fresh random IV to the output.
|
|
89
|
+
*
|
|
90
|
+
* Use this method when the ciphertext will be stored (database, file, etc.)
|
|
91
|
+
* and retrieved later in a different process instance. A new random IV is
|
|
92
|
+
* generated for every call, so repeated encryptions of the same plaintext
|
|
93
|
+
* produce different ciphertexts — no IV reuse across records.
|
|
94
|
+
*
|
|
95
|
+
* Output format: `hex( IV[16 bytes] || ciphertext[N bytes] )`
|
|
96
|
+
*
|
|
97
|
+
* The construction-time IV is **not** used by this method.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} text - The plain text to encrypt.
|
|
100
|
+
* @returns {string} Hex string containing the prepended IV and ciphertext.
|
|
101
|
+
* @see {@link decryptStorable}
|
|
102
|
+
*/
|
|
103
|
+
encryptStorable(text) {
|
|
104
|
+
const iv = randomBytes(16);
|
|
105
|
+
const cipher = createCipheriv('aes-256-ctr', this.key, iv);
|
|
106
|
+
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
|
|
107
|
+
return Buffer.concat([iv, encrypted]).toString('hex');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Decrypts a value produced by {@link encryptStorable}.
|
|
111
|
+
*
|
|
112
|
+
* Extracts the first 16 bytes as the IV, then decrypts the remainder.
|
|
113
|
+
* The construction-time IV is **not** used by this method.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} text - Hex string `IV[16 bytes] || ciphertext` from {@link encryptStorable}.
|
|
116
|
+
* @returns {string} The decrypted plain text.
|
|
117
|
+
* @throws {RangeError} When the input is shorter than 16 bytes (32 hex chars).
|
|
118
|
+
* @see {@link encryptStorable}
|
|
119
|
+
*/
|
|
120
|
+
decryptStorable(text) {
|
|
121
|
+
const buf = Buffer.from(text, 'hex');
|
|
122
|
+
if (buf.length < 16) {
|
|
123
|
+
throw new RangeError(`decryptStorable: input too short (${buf.length} bytes); ` +
|
|
124
|
+
'expected at least 16 bytes for the IV prefix.');
|
|
125
|
+
}
|
|
126
|
+
const iv = buf.subarray(0, 16);
|
|
127
|
+
const ciphertext = buf.subarray(16);
|
|
128
|
+
const decipher = createDecipheriv('aes-256-ctr', this.key, iv);
|
|
129
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
|
|
130
|
+
}
|
|
81
131
|
/**
|
|
82
132
|
* Returns a UUID ver.4 string.
|
|
83
133
|
* @returns {string} UUID ver.4 string.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vvlad1973/crypto",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"license": "MIT with Commercial Use",
|
|
18
18
|
"description": "A JavaScript class for encrypting and decrypting text using specified cryptographic parameters (algorithm, password, etc.)",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^25.
|
|
20
|
+
"@types/node": "^25.3.3",
|
|
21
21
|
"@typescript-eslint/parser": "^7.18.0",
|
|
22
22
|
"@vitest/coverage-v8": "^3.2.4",
|
|
23
23
|
"@vitest/ui": "^3.2.4",
|
package/src/crypto.ts
CHANGED
|
@@ -102,6 +102,10 @@ export class Crypto {
|
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* Encrypt a text string.
|
|
105
|
+
* Uses the IV supplied at construction time (fixed or random-at-construct).
|
|
106
|
+
* The IV is **not** embedded in the output — both sides must use the same IV.
|
|
107
|
+
* For persistent storage use {@link encryptStorable} instead.
|
|
108
|
+
*
|
|
105
109
|
* @param {string} text - The plain text to encrypt.
|
|
106
110
|
* @returns {string} The encrypted text as a hex string.
|
|
107
111
|
*/
|
|
@@ -115,7 +119,9 @@ export class Crypto {
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
/**
|
|
118
|
-
* Decrypt an encrypted text string.
|
|
122
|
+
* Decrypt an encrypted text string produced by {@link encrypt}.
|
|
123
|
+
* Uses the IV supplied at construction time.
|
|
124
|
+
*
|
|
119
125
|
* @param {string} text - The encrypted text as a hex string.
|
|
120
126
|
* @returns {string} The decrypted plain text.
|
|
121
127
|
*/
|
|
@@ -129,6 +135,54 @@ export class Crypto {
|
|
|
129
135
|
return decrypted.toString('utf8');
|
|
130
136
|
}
|
|
131
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Encrypts text and prepends a fresh random IV to the output.
|
|
140
|
+
*
|
|
141
|
+
* Use this method when the ciphertext will be stored (database, file, etc.)
|
|
142
|
+
* and retrieved later in a different process instance. A new random IV is
|
|
143
|
+
* generated for every call, so repeated encryptions of the same plaintext
|
|
144
|
+
* produce different ciphertexts — no IV reuse across records.
|
|
145
|
+
*
|
|
146
|
+
* Output format: `hex( IV[16 bytes] || ciphertext[N bytes] )`
|
|
147
|
+
*
|
|
148
|
+
* The construction-time IV is **not** used by this method.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} text - The plain text to encrypt.
|
|
151
|
+
* @returns {string} Hex string containing the prepended IV and ciphertext.
|
|
152
|
+
* @see {@link decryptStorable}
|
|
153
|
+
*/
|
|
154
|
+
public encryptStorable(text: string): string {
|
|
155
|
+
const iv = randomBytes(16);
|
|
156
|
+
const cipher = createCipheriv('aes-256-ctr', this.key, iv);
|
|
157
|
+
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
|
|
158
|
+
return Buffer.concat([iv, encrypted]).toString('hex');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Decrypts a value produced by {@link encryptStorable}.
|
|
163
|
+
*
|
|
164
|
+
* Extracts the first 16 bytes as the IV, then decrypts the remainder.
|
|
165
|
+
* The construction-time IV is **not** used by this method.
|
|
166
|
+
*
|
|
167
|
+
* @param {string} text - Hex string `IV[16 bytes] || ciphertext` from {@link encryptStorable}.
|
|
168
|
+
* @returns {string} The decrypted plain text.
|
|
169
|
+
* @throws {RangeError} When the input is shorter than 16 bytes (32 hex chars).
|
|
170
|
+
* @see {@link encryptStorable}
|
|
171
|
+
*/
|
|
172
|
+
public decryptStorable(text: string): string {
|
|
173
|
+
const buf = Buffer.from(text, 'hex');
|
|
174
|
+
if (buf.length < 16) {
|
|
175
|
+
throw new RangeError(
|
|
176
|
+
`decryptStorable: input too short (${buf.length} bytes); ` +
|
|
177
|
+
'expected at least 16 bytes for the IV prefix.',
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const iv = buf.subarray(0, 16);
|
|
181
|
+
const ciphertext = buf.subarray(16);
|
|
182
|
+
const decipher = createDecipheriv('aes-256-ctr', this.key, iv);
|
|
183
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
/**
|
|
133
187
|
* Returns a UUID ver.4 string.
|
|
134
188
|
* @returns {string} UUID ver.4 string.
|
package/.vscode/launch.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Используйте IntelliSense, чтобы узнать о возможных атрибутах.
|
|
3
|
-
// Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.
|
|
4
|
-
// Для получения дополнительной информации посетите: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"type": "node",
|
|
9
|
-
"request": "launch",
|
|
10
|
-
"env": {
|
|
11
|
-
"NODE_OPTIONS": "--experimental-vm-modules"
|
|
12
|
-
},
|
|
13
|
-
"name": "Запустить программу",
|
|
14
|
-
"skipFiles": [
|
|
15
|
-
"<node_internals>/**"
|
|
16
|
-
],
|
|
17
|
-
"program": "${workspaceFolder}\\index.js"
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|
package/CLAUDE.md
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Common requirements
|
|
6
|
-
|
|
7
|
-
- отвечай на русском языке. Используй английский только если на то есть особые указания
|
|
8
|
-
- комментарии в коде, в JSDoc - используй английский язык
|
|
9
|
-
- отчеты - используй русский язык
|
|
10
|
-
- при создании документов и commit messages НИКОГДА НЕ ДОБАВЛЯЙ в нихх никакой информации о том, что этот текст сгенерирован тобой, либо при твоем участии/соавторстве
|
|
11
|
-
- при формировании и редактировании файлов *.md соблюдай синтаксис MarkDown. Все Markdown-файлы (*.md) ДОЛЖНЫ проходить проверку линтером без ошибок и предупреждений
|
|
12
|
-
- Не вноси изменений в CHANGELOG.md, этот файл формируется автоматически
|
|
13
|
-
- **НИКОГДА НЕ ИСПОЛЬЗУЙ EMOJI** в файлах .md - это строгое требование проекта без исключений
|
|
14
|
-
- при подготовке файлов документации соблюдай структуру каталога docs, правила, изложенные в C:\Repositories\botApp\bot-app-service\docs\CONTRIBUTING.md
|
|
15
|
-
|
|
16
|
-
## Project Overview
|
|
17
|
-
|
|
18
|
-
TypeScript library providing AES-256-CTR encryption/decryption functionality. Wrapper around Node.js native `crypto` module, replacing the previous `aes-js` dependency.
|
|
19
|
-
|
|
20
|
-
## Development Commands
|
|
21
|
-
|
|
22
|
-
### Build
|
|
23
|
-
```bash
|
|
24
|
-
npm run build
|
|
25
|
-
```
|
|
26
|
-
Compiles TypeScript to JavaScript using `tsc`. Output: `dist/` directory with `.js` and `.d.ts` files.
|
|
27
|
-
|
|
28
|
-
### Testing
|
|
29
|
-
```bash
|
|
30
|
-
npm test
|
|
31
|
-
```
|
|
32
|
-
Runs build followed by Node.js native test runner with ts-node loader.
|
|
33
|
-
|
|
34
|
-
To run a specific test file:
|
|
35
|
-
```bash
|
|
36
|
-
npm run build && node --loader ts-node/esm --test src/__tests__/crypto.test.ts
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Documentation Generation
|
|
40
|
-
```bash
|
|
41
|
-
npm run doc
|
|
42
|
-
```
|
|
43
|
-
Generates JSDoc documentation using better-docs template. Output: `docs/` directory.
|
|
44
|
-
|
|
45
|
-
## Architecture
|
|
46
|
-
|
|
47
|
-
### Core Module Structure
|
|
48
|
-
- [src/crypto.ts](src/crypto.ts) - Main `Crypto` class implementation
|
|
49
|
-
- [src/index.ts](src/index.ts) - Module exports
|
|
50
|
-
- [src/__tests__/crypto.test.ts](src/__tests__/crypto.test.ts) - Test suite
|
|
51
|
-
|
|
52
|
-
### Crypto Class Design
|
|
53
|
-
|
|
54
|
-
The `Crypto` class uses Node.js native crypto APIs:
|
|
55
|
-
- **Key derivation**: `pbkdf2Sync` with configurable algorithm (default SHA512), iterations (1000), and key length (32 bytes)
|
|
56
|
-
- **Encryption**: AES-256-CTR mode via `createCipheriv`
|
|
57
|
-
- **Initialization Vector (IV)**: Accepts either Buffer or number (converted to 16-byte Buffer)
|
|
58
|
-
|
|
59
|
-
Constructor supports two signatures:
|
|
60
|
-
1. Individual parameters: `new Crypto(password, salt, algorithm?, iterations?, keyLength?, iv?)`
|
|
61
|
-
2. Options object: `new Crypto({ password, salt, algorithm?, iterations?, keyLength?, iv? })`
|
|
62
|
-
|
|
63
|
-
### Key Implementation Details
|
|
64
|
-
|
|
65
|
-
- IV conversion from number: Uses `Buffer.alloc(16)` with `writeUInt32BE` at offset 12
|
|
66
|
-
- Encryption output: Hex-encoded string
|
|
67
|
-
- Type guard: `isCrypto(object)` function checks for `encrypt` and `decrypt` methods
|
|
68
|
-
- Static method: `Crypto.getUUID()` returns UUIDv4 string using `crypto.randomUUID()`
|
|
69
|
-
|
|
70
|
-
### Module System
|
|
71
|
-
- ES modules (`"type": "module"` in package.json)
|
|
72
|
-
- TypeScript target: ES2022
|
|
73
|
-
- File extensions: Import paths must use `.js` extension (not `.ts`) due to ES module resolution
|
|
74
|
-
|
|
75
|
-
## TypeScript Configuration
|
|
76
|
-
|
|
77
|
-
Strict mode enabled with full type checking. Output includes declaration files for TypeScript consumers.
|
|
78
|
-
|
|
79
|
-
## Testing Approach
|
|
80
|
-
|
|
81
|
-
Tests use Node.js native test runner (`node:test`) with:
|
|
82
|
-
- Encryption/decryption round-trip verification
|
|
83
|
-
- Both constructor signatures
|
|
84
|
-
- Default parameter handling
|
|
85
|
-
- Type guard validation
|
|
86
|
-
- Backward compatibility with previous library version (validates specific encrypted output)
|
|
87
|
-
|
|
88
|
-
## JSDoc Requirements
|
|
89
|
-
|
|
90
|
-
Per user instructions: All modules must include complete JSDoc with module definitions. Comments and JSDoc must be in English.
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach, expect } from 'vitest';
|
|
2
|
-
import { randomBytes } from 'crypto';
|
|
3
|
-
import Crypto, { CryptoOptions, isCrypto } from '../crypto.js';
|
|
4
|
-
|
|
5
|
-
describe('Crypto', () => {
|
|
6
|
-
const plainText = 'Hello, World!';
|
|
7
|
-
let options: CryptoOptions;
|
|
8
|
-
let password: string;
|
|
9
|
-
let salt: string;
|
|
10
|
-
let algorithm: string;
|
|
11
|
-
let iterations: number;
|
|
12
|
-
let keyLength: number;
|
|
13
|
-
let iv: number;
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
options = {
|
|
17
|
-
password: 'testPassword',
|
|
18
|
-
salt: 'testSalt',
|
|
19
|
-
algorithm: 'SHA512',
|
|
20
|
-
iterations: 1000,
|
|
21
|
-
keyLength: 32,
|
|
22
|
-
iv: 5,
|
|
23
|
-
};
|
|
24
|
-
password = 'testPassword';
|
|
25
|
-
salt = 'testSalt';
|
|
26
|
-
algorithm = 'SHA512';
|
|
27
|
-
iterations = 1000;
|
|
28
|
-
keyLength = 32;
|
|
29
|
-
iv = 5;
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should initialize with CryptoOptions object', () => {
|
|
33
|
-
const crypto = new Crypto(options);
|
|
34
|
-
expect(crypto).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should initialize with separate parameters', () => {
|
|
38
|
-
const crypto = new Crypto(
|
|
39
|
-
password,
|
|
40
|
-
salt,
|
|
41
|
-
algorithm,
|
|
42
|
-
iterations,
|
|
43
|
-
keyLength,
|
|
44
|
-
iv
|
|
45
|
-
);
|
|
46
|
-
expect(crypto).toBeDefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should return UUIDv4', () => {
|
|
50
|
-
const crypto = new Crypto(options);
|
|
51
|
-
const uuid = Crypto.getUUID();
|
|
52
|
-
const uuidv4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
53
|
-
expect(uuid).toMatch(uuidv4Regex);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should encrypt and decrypt text correctly using ICryptoOptions', () => {
|
|
57
|
-
const crypto = new Crypto(options);
|
|
58
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
59
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
60
|
-
expect(decryptedText).toBe(plainText);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should encrypt and decrypt text correctly using separate parameters', () => {
|
|
64
|
-
const crypto = new Crypto(
|
|
65
|
-
password,
|
|
66
|
-
salt,
|
|
67
|
-
algorithm,
|
|
68
|
-
iterations,
|
|
69
|
-
keyLength,
|
|
70
|
-
iv
|
|
71
|
-
);
|
|
72
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
73
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
74
|
-
expect(decryptedText).toBe(plainText);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should handle default values for optional parameters', () => {
|
|
78
|
-
const cryptoWithDefaults = new Crypto(password, salt);
|
|
79
|
-
const encryptedText = cryptoWithDefaults.encrypt(plainText);
|
|
80
|
-
const decryptedText = cryptoWithDefaults.decrypt(encryptedText);
|
|
81
|
-
expect(decryptedText).toBe(plainText);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should initialize with Buffer IV using options object', () => {
|
|
85
|
-
const ivBuffer = randomBytes(16);
|
|
86
|
-
const crypto = new Crypto({
|
|
87
|
-
password,
|
|
88
|
-
salt,
|
|
89
|
-
algorithm,
|
|
90
|
-
iterations,
|
|
91
|
-
keyLength,
|
|
92
|
-
iv: ivBuffer,
|
|
93
|
-
});
|
|
94
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
95
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
96
|
-
expect(decryptedText).toBe(plainText);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should initialize with Buffer IV using separate parameters', () => {
|
|
100
|
-
const ivBuffer = randomBytes(16);
|
|
101
|
-
const crypto = new Crypto(
|
|
102
|
-
password,
|
|
103
|
-
salt,
|
|
104
|
-
algorithm,
|
|
105
|
-
iterations,
|
|
106
|
-
keyLength,
|
|
107
|
-
ivBuffer
|
|
108
|
-
);
|
|
109
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
110
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
111
|
-
expect(decryptedText).toBe(plainText);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should initialize without IV (random generation via options)', () => {
|
|
115
|
-
const crypto1 = new Crypto({ password, salt });
|
|
116
|
-
const crypto2 = new Crypto({ password, salt });
|
|
117
|
-
const encrypted1 = crypto1.encrypt(plainText);
|
|
118
|
-
const encrypted2 = crypto2.encrypt(plainText);
|
|
119
|
-
// Should produce different encrypted results due to different random IVs
|
|
120
|
-
expect(encrypted1).not.toBe(encrypted2);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should handle empty string encryption and decryption', () => {
|
|
124
|
-
const crypto = new Crypto(options);
|
|
125
|
-
const emptyText = '';
|
|
126
|
-
const encryptedText = crypto.encrypt(emptyText);
|
|
127
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
128
|
-
expect(decryptedText).toBe(emptyText);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should handle Unicode characters', () => {
|
|
132
|
-
const crypto = new Crypto(options);
|
|
133
|
-
const unicodeText = '🔐 Encryption test 中文 العربية';
|
|
134
|
-
const encryptedText = crypto.encrypt(unicodeText);
|
|
135
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
136
|
-
expect(decryptedText).toBe(unicodeText);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should handle different hash algorithms', () => {
|
|
140
|
-
const crypto256 = new Crypto(password, salt, 'sha256', iterations, keyLength, iv);
|
|
141
|
-
const encryptedText = crypto256.encrypt(plainText);
|
|
142
|
-
const decryptedText = crypto256.decrypt(encryptedText);
|
|
143
|
-
expect(decryptedText).toBe(plainText);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should handle different key lengths', () => {
|
|
147
|
-
// AES-256-CTR requires 32 bytes key, testing with valid 32 bytes
|
|
148
|
-
const crypto32 = new Crypto(password, salt, algorithm, iterations, 32, iv);
|
|
149
|
-
const encryptedText = crypto32.encrypt(plainText);
|
|
150
|
-
const decryptedText = crypto32.decrypt(encryptedText);
|
|
151
|
-
expect(decryptedText).toBe(plainText);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should handle different iteration counts', () => {
|
|
155
|
-
const crypto = new Crypto(password, salt, algorithm, 5000, keyLength, iv);
|
|
156
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
157
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
158
|
-
expect(decryptedText).toBe(plainText);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('isCrypto', () => {
|
|
163
|
-
const validCryptoOptions: CryptoOptions = {
|
|
164
|
-
password: 'testPassword',
|
|
165
|
-
salt: 'testSalt',
|
|
166
|
-
algorithm: 'SHA512',
|
|
167
|
-
iterations: 1000,
|
|
168
|
-
keyLength: 32,
|
|
169
|
-
iv: 5,
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
it('should return true for a valid Crypto instance created with ICryptoOptions', () => {
|
|
173
|
-
const crypto = new Crypto(validCryptoOptions);
|
|
174
|
-
expect(isCrypto(crypto)).toBe(true);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should return true for a valid Crypto instance created with separate parameters', () => {
|
|
178
|
-
const crypto = new Crypto(
|
|
179
|
-
validCryptoOptions.password,
|
|
180
|
-
validCryptoOptions.salt,
|
|
181
|
-
validCryptoOptions.algorithm,
|
|
182
|
-
validCryptoOptions.iterations,
|
|
183
|
-
validCryptoOptions.keyLength,
|
|
184
|
-
validCryptoOptions.iv
|
|
185
|
-
);
|
|
186
|
-
expect(isCrypto(crypto)).toBe(true);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should return false for null', () => {
|
|
190
|
-
const nullObject = null;
|
|
191
|
-
expect(isCrypto(nullObject)).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('should return false for undefined', () => {
|
|
195
|
-
const undefinedObject = undefined;
|
|
196
|
-
expect(isCrypto(undefinedObject)).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should return false for a plain object without encrypt and decrypt methods', () => {
|
|
200
|
-
const plainObject = {
|
|
201
|
-
someMethod: () => {},
|
|
202
|
-
};
|
|
203
|
-
expect(isCrypto(plainObject)).toBe(false);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('Compatibility with previous version', () => {
|
|
208
|
-
|
|
209
|
-
const plainText = 'Проверка связи';
|
|
210
|
-
let options: CryptoOptions;
|
|
211
|
-
let encText = 'a4661cb701751a895f65418ed488faad33da3090d05ad661f0a7a9';
|
|
212
|
-
|
|
213
|
-
beforeEach(() => {
|
|
214
|
-
options = {
|
|
215
|
-
algorithm: 'SHA512',
|
|
216
|
-
iv: 5,
|
|
217
|
-
keyLength: 32,
|
|
218
|
-
iterations: 1000,
|
|
219
|
-
password: 'password',
|
|
220
|
-
salt: 'saltsaltsalt',
|
|
221
|
-
};
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('should return correct string', () => {
|
|
225
|
-
const crypto = new Crypto(options);
|
|
226
|
-
|
|
227
|
-
const decryptedText = crypto.decrypt(encText);
|
|
228
|
-
expect(decryptedText).toBe(plainText);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('should return correct encrypt-decrypt', () => {
|
|
232
|
-
const crypto = new Crypto(options);
|
|
233
|
-
|
|
234
|
-
const encryptedText = crypto.encrypt(plainText);
|
|
235
|
-
const decryptedText = crypto.decrypt(encryptedText);
|
|
236
|
-
expect(decryptedText).toBe(plainText);
|
|
237
|
-
});
|
|
238
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"outDir": "./dist",
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"skipLibCheck": true
|
|
11
|
-
},
|
|
12
|
-
"include": [
|
|
13
|
-
"./src/**/*"
|
|
14
|
-
],
|
|
15
|
-
"exclude": [
|
|
16
|
-
"node_modules",
|
|
17
|
-
"dist",
|
|
18
|
-
"coverage",
|
|
19
|
-
"docs"
|
|
20
|
-
]
|
|
21
|
-
}
|
package/vitest.config.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
resolve: {
|
|
5
|
-
extensions: ['.ts', '.js']
|
|
6
|
-
},
|
|
7
|
-
test: {
|
|
8
|
-
environment: 'node',
|
|
9
|
-
coverage: {
|
|
10
|
-
provider: 'v8',
|
|
11
|
-
reporter: ['text', 'html', 'lcov', 'json'],
|
|
12
|
-
reportsDirectory: './coverage',
|
|
13
|
-
clean: true,
|
|
14
|
-
exclude: [
|
|
15
|
-
'node_modules/**',
|
|
16
|
-
'dist/**',
|
|
17
|
-
'docs/**',
|
|
18
|
-
'coverage/**',
|
|
19
|
-
'**/__tests__/**',
|
|
20
|
-
'**/*.test.ts',
|
|
21
|
-
'**/*.spec.ts',
|
|
22
|
-
'**/*.config.ts',
|
|
23
|
-
'**/index.ts'
|
|
24
|
-
],
|
|
25
|
-
include: ['src/crypto.ts'],
|
|
26
|
-
thresholds: {
|
|
27
|
-
lines: 90,
|
|
28
|
-
functions: 85,
|
|
29
|
-
branches: 90,
|
|
30
|
-
statements: 90
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
include: ['src/**/__tests__/**/*.test.ts'],
|
|
34
|
-
exclude: ['node_modules', 'dist', 'docs']
|
|
35
|
-
}
|
|
36
|
-
});
|