@venizia/ignis-docs 0.2.0 → 0.2.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -1,537 +1,124 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| Class | Extends | Use Case |
|
|
8
|
-
|-------|---------|----------|
|
|
9
|
-
| **AES** | BaseCryptoAlgorithm | Fast symmetric encryption (AES-256-CBC, AES-256-GCM) |
|
|
10
|
-
| **RSA** | BaseCryptoAlgorithm | Public-key encryption with DER key pairs |
|
|
11
|
-
| **ECDH** | AbstractCryptoAlgorithm | Ephemeral key exchange with AES-256-GCM session encryption |
|
|
12
|
-
| **hash()** | _(standalone function)_ | MD5 and SHA256 HMAC hashing |
|
|
13
|
-
|
|
14
|
-
#### Algorithm Comparison
|
|
15
|
-
|
|
16
|
-
| Feature | AES | RSA | ECDH |
|
|
17
|
-
|---------|-----|-----|------|
|
|
18
|
-
| Type | Symmetric | Asymmetric | Asymmetric + Symmetric |
|
|
19
|
-
| Key exchange | Shared secret | Public/private | Diffie-Hellman |
|
|
20
|
-
| Speed | Fast | Slow (large keys) | Fast (small keys) |
|
|
21
|
-
| Max message | Unlimited | ~214 bytes (2048-bit, OAEP) | Unlimited |
|
|
22
|
-
| Async | No | No | Yes (Web Crypto) |
|
|
23
|
-
| Runtime | Node.js `crypto` | Node.js `crypto` | `crypto.subtle` (Bun/Browser) |
|
|
24
|
-
|
|
25
|
-
#### Import Paths
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// Algorithm classes
|
|
29
|
-
import { AES, RSA, ECDH } from '@venizia/ignis-helpers';
|
|
30
|
-
|
|
31
|
-
// Hash utility function
|
|
32
|
-
import { hash } from '@venizia/ignis-helpers';
|
|
33
|
-
|
|
34
|
-
// Types
|
|
35
|
-
import type {
|
|
36
|
-
AESAlgorithmType,
|
|
37
|
-
RSAAlgorithmType,
|
|
38
|
-
ECDHAlgorithmType,
|
|
39
|
-
IECDHEncryptedPayload,
|
|
40
|
-
IECDHExtraOptions,
|
|
41
|
-
ICryptoAlgorithm,
|
|
42
|
-
} from '@venizia/ignis-helpers';
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
#### Type Hierarchy
|
|
46
|
-
|
|
47
|
-
All crypto algorithms share a common type hierarchy with 7 generic type parameters:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
ICryptoAlgorithm (interface)
|
|
51
|
-
└── AbstractCryptoAlgorithm (extends BaseHelper)
|
|
52
|
-
├── BaseCryptoAlgorithm (adds normalizeSecretKey, getAlgorithmKeySize)
|
|
53
|
-
│ ├── AES
|
|
54
|
-
│ └── RSA
|
|
55
|
-
└── ECDH (uses CryptoKey objects, not string secrets)
|
|
56
|
-
```
|
|
1
|
+
---
|
|
2
|
+
title: Crypto
|
|
3
|
+
description: AES symmetric encryption, RSA asymmetric encryption, and ECDH key exchange helpers
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
57
6
|
|
|
58
|
-
|
|
59
|
-
- `BaseCryptoAlgorithm` adds `normalizeSecretKey()` and `getAlgorithmKeySize()` -- useful for AES/RSA which use string secrets with size normalization
|
|
60
|
-
- `ECDH` extends `AbstractCryptoAlgorithm` directly because it uses `CryptoKey` objects (Web Crypto), not string secrets
|
|
7
|
+
# Crypto
|
|
61
8
|
|
|
62
|
-
|
|
63
|
-
interface ICryptoAlgorithm<
|
|
64
|
-
AlgorithmNameType extends string,
|
|
65
|
-
EncryptInputType = unknown,
|
|
66
|
-
DecryptInputType = unknown,
|
|
67
|
-
SecretKeyType = unknown,
|
|
68
|
-
EncryptReturnType = unknown,
|
|
69
|
-
DecryptReturnType = unknown,
|
|
70
|
-
ExtraOptions = unknown,
|
|
71
|
-
> {
|
|
72
|
-
algorithm: AlgorithmNameType;
|
|
73
|
-
encrypt(opts: { message: EncryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): EncryptReturnType;
|
|
74
|
-
decrypt(opts: { message: DecryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): DecryptReturnType;
|
|
75
|
-
}
|
|
76
|
-
```
|
|
9
|
+
Cryptographic helpers for AES symmetric encryption, RSA asymmetric encryption, and ECDH ephemeral key exchange, each wrapped in a scoped `BaseHelper` class.
|
|
77
10
|
|
|
78
|
-
##
|
|
11
|
+
## In one example
|
|
79
12
|
|
|
80
|
-
|
|
13
|
+
The smallest real use: encrypt and decrypt a string with AES-256-GCM.
|
|
81
14
|
|
|
82
15
|
```typescript
|
|
83
|
-
import { AES
|
|
84
|
-
|
|
85
|
-
// AES -- choose CBC or GCM mode
|
|
86
|
-
const aesCbc = AES.withAlgorithm('aes-256-cbc');
|
|
87
|
-
const aesGcm = AES.withAlgorithm('aes-256-gcm');
|
|
88
|
-
|
|
89
|
-
// RSA -- single algorithm, no parameters
|
|
90
|
-
const rsa = RSA.withAlgorithm();
|
|
91
|
-
|
|
92
|
-
// ECDH -- optional HKDF info for key isolation
|
|
93
|
-
const ecdh = ECDH.withAlgorithm();
|
|
94
|
-
const ecdhCustom = ECDH.withAlgorithm({
|
|
95
|
-
algorithm: 'ecdh-p256',
|
|
96
|
-
hkdfInfo: 'my-app-session-keys',
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### AES Constructor Options
|
|
101
|
-
|
|
102
|
-
| Algorithm | Mode | Features |
|
|
103
|
-
|-----------|------|----------|
|
|
104
|
-
| `aes-256-cbc` | CBC | Standard block cipher, widely compatible |
|
|
105
|
-
| `aes-256-gcm` | GCM | Authenticated encryption -- detects tampering |
|
|
106
|
-
|
|
107
|
-
#### ECDH Constructor Options
|
|
108
|
-
|
|
109
|
-
| Option | Type | Default | Description |
|
|
110
|
-
|--------|------|---------|-------------|
|
|
111
|
-
| `algorithm` | `'ecdh-p256'` | `'ecdh-p256'` | Curve algorithm |
|
|
112
|
-
| `hkdfInfo` | `string` | `'ignis-ecdh-p256-aes-256-gcm-v1'` | HKDF info string for key derivation isolation |
|
|
113
|
-
|
|
114
|
-
Different `hkdfInfo` values produce **incompatible keys** from the same ECDH shared secret. Use this to isolate key derivation between different application contexts.
|
|
16
|
+
import { AES } from '@venizia/ignis-helpers';
|
|
115
17
|
|
|
116
|
-
## Usage
|
|
117
|
-
|
|
118
|
-
### AES Encryption
|
|
119
|
-
|
|
120
|
-
The `AES` class provides encryption and decryption using the Advanced Encryption Standard with 256-bit keys.
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
18
|
const aes = AES.withAlgorithm('aes-256-gcm');
|
|
124
19
|
const secret = 'my-application-secret-key';
|
|
125
20
|
|
|
126
|
-
// Encrypt
|
|
127
21
|
const encrypted = aes.encrypt({ message: 'This is a secret message.', secret });
|
|
128
|
-
// => base64 encoded string containing IV + ciphertext
|
|
22
|
+
// => base64 encoded string containing IV + auth tag + ciphertext
|
|
129
23
|
|
|
130
|
-
// Decrypt
|
|
131
24
|
const decrypted = aes.decrypt({ message: encrypted, secret });
|
|
132
25
|
// => 'This is a secret message.'
|
|
133
26
|
```
|
|
134
27
|
|
|
135
|
-
|
|
136
|
-
> Prefer `aes-256-gcm` for new applications. It provides **authenticated encryption** -- if the ciphertext is tampered with, decryption will throw an error rather than silently returning corrupted data. This does not happen with CBC mode.
|
|
137
|
-
|
|
138
|
-
#### AES Extra Options
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
import C from 'node:crypto';
|
|
142
|
-
|
|
143
|
-
const encrypted = aes.encrypt({
|
|
144
|
-
message: 'hello',
|
|
145
|
-
secret: 'my-secret',
|
|
146
|
-
opts: {
|
|
147
|
-
iv: C.randomBytes(16), // Custom IV (default: random 16 bytes)
|
|
148
|
-
inputEncoding: 'utf-8', // Message input encoding (default: 'utf-8')
|
|
149
|
-
outputEncoding: 'hex', // Ciphertext output encoding (default: 'base64')
|
|
150
|
-
doThrow: false, // Return original message on error (default: true)
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
| Option | Type | Default (encrypt) | Default (decrypt) | Description |
|
|
156
|
-
|--------|------|-------------------|-------------------|-------------|
|
|
157
|
-
| `iv` | `Buffer` | `crypto.randomBytes(16)` | Extracted from ciphertext | Initialization vector |
|
|
158
|
-
| `inputEncoding` | `crypto.Encoding` | `'utf-8'` | `'base64'` | Encoding of the input message |
|
|
159
|
-
| `outputEncoding` | `crypto.Encoding` | `'base64'` | `'utf-8'` | Encoding of the output |
|
|
160
|
-
| `doThrow` | `boolean` | `true` | `true` | If `false`, returns the original message on error instead of throwing |
|
|
161
|
-
|
|
162
|
-
#### File Encryption
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
// Encrypt file contents -> returns encrypted string
|
|
166
|
-
const encrypted = aes.encryptFile({
|
|
167
|
-
absolutePath: '/path/to/config.json',
|
|
168
|
-
secret: 'my-secret',
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// Decrypt file contents -> returns decrypted string
|
|
172
|
-
const decrypted = aes.decryptFile({
|
|
173
|
-
absolutePath: '/path/to/config.json.enc',
|
|
174
|
-
secret: 'my-secret',
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Both methods read the file synchronously via `fs.readFileSync`, convert to UTF-8, then encrypt/decrypt as a string. If `absolutePath` is empty or falsy, they return an empty string.
|
|
28
|
+
`RSA` and `ECDH` follow the same `withAlgorithm()` factory + `encrypt`/`decrypt` shape. Only the secret type and the speed/message-size trade-offs differ.
|
|
179
29
|
|
|
180
|
-
|
|
30
|
+
## How it works
|
|
181
31
|
|
|
182
|
-
|
|
32
|
+
- **One factory pattern.** Every algorithm class exposes a static `withAlgorithm()` that returns an instance. There is no public constructor to call directly.
|
|
33
|
+
- **`ECDH` extends the neutral `AbstractCryptoAlgorithm` directly.** It uses `CryptoKey` objects from the Web Crypto API (`crypto.subtle`), not string secrets. It skips the string-normalization helpers entirely.
|
|
34
|
+
- **Options objects, throw-by-default.** Every `encrypt`/`decrypt` takes `{ message, secret, opts? }`. On internal error, each throws by default. Pass `opts.doThrow: false` to get the original input back unchanged instead.
|
|
183
35
|
|
|
184
|
-
|
|
36
|
+
**`AES` and `RSA` share `BaseCryptoAlgorithm`**, which adds two helpers:
|
|
185
37
|
|
|
186
|
-
|
|
38
|
+
| Method | Does |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `normalizeSecretKey()` | Pads or truncates a string secret to the algorithm's key size |
|
|
41
|
+
| `getAlgorithmKeySize()` | Parses the bit size out of the algorithm name - `256` from `aes-256-gcm` |
|
|
187
42
|
|
|
188
|
-
|
|
189
|
-
const rsa = RSA.withAlgorithm();
|
|
43
|
+
**Class comparison**
|
|
190
44
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
```
|
|
45
|
+
| Class | Base class | Secret type | Async | Best for |
|
|
46
|
+
|-------|-----------|--------------|-------|----------|
|
|
47
|
+
| `AES` | `BaseCryptoAlgorithm` | `string` | No | Encrypting data at rest, fast bulk encryption |
|
|
48
|
+
| `RSA` | `BaseCryptoAlgorithm` | `string` (base64 DER key) | No | Public-key encryption, small payloads |
|
|
49
|
+
| `ECDH` | `AbstractCryptoAlgorithm` | `CryptoKey` | Yes | Session key exchange with forward secrecy |
|
|
197
50
|
|
|
198
|
-
|
|
199
|
-
|--------|------|---------|-------------|
|
|
200
|
-
| `modulus` | `number` | `2048` | RSA modulus length in bits |
|
|
51
|
+
`AES` supports two modes selected at construction: `aes-256-cbc` (plain block cipher) and `aes-256-gcm` (authenticated - detects tampering). Everything on this page uses the default options.
|
|
201
52
|
|
|
202
|
-
|
|
53
|
+
See the [Full reference](/extensions/helpers/crypto/reference) for every option, the ECDH key-exchange flow, `IECDHEncryptedPayload`, and the standalone `hash()` utility.
|
|
203
54
|
|
|
204
|
-
|
|
55
|
+
## Common tasks
|
|
205
56
|
|
|
206
|
-
|
|
207
|
-
// Encrypt using the public key (base64-encoded DER)
|
|
208
|
-
const pubKeyB64 = publicKey.toString('base64');
|
|
209
|
-
const encrypted = rsa.encrypt({ message: 'secret data', secret: pubKeyB64 });
|
|
210
|
-
|
|
211
|
-
// Decrypt using the private key (base64-encoded DER)
|
|
212
|
-
const privKeyB64 = privateKey.toString('base64');
|
|
213
|
-
const decrypted = rsa.decrypt({ message: encrypted, secret: privKeyB64 });
|
|
214
|
-
// => 'secret data'
|
|
215
|
-
```
|
|
57
|
+
### Choose an AES mode
|
|
216
58
|
|
|
217
|
-
|
|
59
|
+
`aes-256-gcm` is authenticated encryption - tampering makes decryption throw instead of returning corrupted data. Prefer it for new code.
|
|
218
60
|
|
|
219
61
|
```typescript
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
secret: pubKeyB64,
|
|
223
|
-
opts: {
|
|
224
|
-
inputEncoding: {
|
|
225
|
-
key: 'base64', // Key encoding (default: 'base64')
|
|
226
|
-
message: 'utf-8', // Message encoding (default: 'utf-8')
|
|
227
|
-
},
|
|
228
|
-
outputEncoding: 'hex', // Ciphertext output (default: 'base64')
|
|
229
|
-
doThrow: false, // Return original on error (default: true)
|
|
230
|
-
},
|
|
231
|
-
});
|
|
62
|
+
const aesGcm = AES.withAlgorithm('aes-256-gcm'); // recommended
|
|
63
|
+
const aesCbc = AES.withAlgorithm('aes-256-cbc'); // no tamper detection
|
|
232
64
|
```
|
|
233
65
|
|
|
234
|
-
|
|
235
|
-
|--------|------|-------------------|-------------------|-------------|
|
|
236
|
-
| `inputEncoding.key` | `crypto.Encoding` | `'base64'` | `'base64'` | Encoding of the key buffer |
|
|
237
|
-
| `inputEncoding.message` | `crypto.Encoding` | `'utf-8'` | `'base64'` | Encoding of the input message |
|
|
238
|
-
| `outputEncoding` | `crypto.Encoding` | `'base64'` | `'utf-8'` | Encoding of the output |
|
|
239
|
-
| `doThrow` | `boolean` | `true` | `true` | If `false`, returns the original message on error instead of throwing |
|
|
66
|
+
### Encrypt a file
|
|
240
67
|
|
|
241
|
-
|
|
68
|
+
`encryptFile`/`decryptFile` read the file synchronously, treat its contents as UTF-8, then run the same `encrypt`/`decrypt` as strings.
|
|
242
69
|
|
|
243
70
|
```typescript
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
rsa.encrypt({ message: 'test', secret: 'invalid-key' });
|
|
247
|
-
} catch (error) {
|
|
248
|
-
// Handle encryption error
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Graceful: return original message on error
|
|
252
|
-
const result = rsa.encrypt({
|
|
253
|
-
message: 'test',
|
|
254
|
-
secret: 'invalid-key',
|
|
255
|
-
opts: { doThrow: false },
|
|
256
|
-
});
|
|
257
|
-
// result === 'test' (original message returned)
|
|
71
|
+
const encrypted = aes.encryptFile({ absolutePath: '/path/to/config.json', secret });
|
|
72
|
+
const decrypted = aes.decryptFile({ absolutePath: '/path/to/config.json.enc', secret });
|
|
258
73
|
```
|
|
259
74
|
|
|
260
|
-
###
|
|
261
|
-
|
|
262
|
-
The `ECDH` class provides **ephemeral key exchange** using ECDH P-256 with HKDF-derived AES-256-GCM session encryption. It uses the Web Crypto API (`crypto.subtle`) and is fully async.
|
|
263
|
-
|
|
264
|
-
#### When to Use ECDH
|
|
265
|
-
|
|
266
|
-
| Scenario | Use ECDH | Use AES/RSA |
|
|
267
|
-
|----------|----------|-------------|
|
|
268
|
-
| Two parties need a shared secret without pre-sharing | Yes | No |
|
|
269
|
-
| WebSocket session encryption | Yes | No |
|
|
270
|
-
| Encrypting data at rest | No | AES |
|
|
271
|
-
| Signing/verifying tokens | No | RSA |
|
|
272
|
-
| Forward secrecy needed | Yes | No |
|
|
75
|
+
### Generate an RSA key pair and encrypt with it
|
|
273
76
|
|
|
274
|
-
|
|
77
|
+
Keys are DER-encoded (`SPKI` public, `PKCS8` private). Base64-encode them to pass as the `secret` string.
|
|
275
78
|
|
|
276
79
|
```typescript
|
|
277
|
-
|
|
80
|
+
import { RSA } from '@venizia/ignis-helpers';
|
|
278
81
|
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
const bob = await ecdh.generateKeyPair();
|
|
282
|
-
|
|
283
|
-
// 2. Exchange public keys (over any channel -- they're safe to share)
|
|
284
|
-
const alicePubForBob = await ecdh.importPublicKey({ rawKeyB64: alice.publicKeyB64 });
|
|
285
|
-
const bobPubForAlice = await ecdh.importPublicKey({ rawKeyB64: bob.publicKeyB64 });
|
|
286
|
-
|
|
287
|
-
// 3. Initiator derives AES key (generates a random salt)
|
|
288
|
-
const { key: aliceKey, salt } = await ecdh.deriveAESKey({
|
|
289
|
-
privateKey: alice.keyPair.privateKey,
|
|
290
|
-
peerPublicKey: bobPubForAlice,
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// 4. Responder derives the SAME AES key using the initiator's salt
|
|
294
|
-
const { key: bobKey } = await ecdh.deriveAESKey({
|
|
295
|
-
privateKey: bob.keyPair.privateKey,
|
|
296
|
-
peerPublicKey: alicePubForBob,
|
|
297
|
-
salt, // Must use the same salt for keys to match
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// 5. Alice encrypts -> Bob decrypts (or vice versa)
|
|
301
|
-
const encrypted = await ecdh.encrypt({ message: 'Hello Bob!', secret: aliceKey });
|
|
302
|
-
const decrypted = await ecdh.decrypt({ message: encrypted, secret: bobKey });
|
|
303
|
-
// => 'Hello Bob!'
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
> [!IMPORTANT]
|
|
307
|
-
> Both parties **must** use the same salt for `deriveAESKey` to produce matching keys. The initiator omits the `salt` parameter (a random 32-byte salt is generated), then shares the returned `salt` string with the responder.
|
|
308
|
-
|
|
309
|
-
#### Key Generation and Import
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
// Generate a key pair
|
|
313
|
-
const { keyPair, publicKeyB64 } = await ecdh.generateKeyPair();
|
|
314
|
-
// keyPair.publicKey -- CryptoKey (exported as raw base64 via publicKeyB64)
|
|
315
|
-
// keyPair.privateKey -- CryptoKey (non-extractable)
|
|
316
|
-
// publicKeyB64 -- base64 encoded raw public key (65 bytes for P-256)
|
|
317
|
-
|
|
318
|
-
// Import a peer's base64-encoded public key
|
|
319
|
-
const peerKey = await ecdh.importPublicKey({ rawKeyB64: peerPublicKeyB64 });
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
#### AES Key Derivation
|
|
323
|
-
|
|
324
|
-
The derived key uses **HKDF** (HMAC-based Key Derivation Function) with SHA-256 to produce an AES-256-GCM key from the ECDH shared secret. A random 32-byte salt is generated if not provided.
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
// Initiator: omit salt (random salt is generated)
|
|
328
|
-
const { key: aesKey, salt } = await ecdh.deriveAESKey({
|
|
329
|
-
privateKey: myKeyPair.privateKey,
|
|
330
|
-
peerPublicKey: importedPeerPublicKey,
|
|
331
|
-
});
|
|
332
|
-
// aesKey -- CryptoKey for AES-256-GCM (non-extractable, encrypt + decrypt)
|
|
333
|
-
// salt -- base64 encoded 32-byte salt (share with peer)
|
|
334
|
-
|
|
335
|
-
// Responder: provide the initiator's salt
|
|
336
|
-
const { key: peerAesKey } = await ecdh.deriveAESKey({
|
|
337
|
-
privateKey: peerKeyPair.privateKey,
|
|
338
|
-
peerPublicKey: importedMyPublicKey,
|
|
339
|
-
salt, // Same salt -> same derived key
|
|
340
|
-
});
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
#### `deriveAESKey` Options
|
|
344
|
-
|
|
345
|
-
| Option | Type | Default | Description |
|
|
346
|
-
|--------|------|---------|-------------|
|
|
347
|
-
| `privateKey` | `CryptoKey` | -- | Your ECDH private key from `generateKeyPair()` |
|
|
348
|
-
| `peerPublicKey` | `CryptoKey` | -- | Peer's public key from `importPublicKey()` |
|
|
349
|
-
| `salt` | `string` | Random 32 bytes | Base64-encoded salt for HKDF. Omit to generate a new random salt. |
|
|
350
|
-
|
|
351
|
-
#### Additional Authenticated Data (AAD)
|
|
352
|
-
|
|
353
|
-
ECDH encrypt/decrypt supports **Additional Authenticated Data** via the `opts.additionalData` parameter. AAD is authenticated but not encrypted -- it binds the ciphertext to a context (e.g., channel ID, session ID) so the same ciphertext cannot be replayed in a different context.
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
// Encrypt with AAD
|
|
357
|
-
const encrypted = await ecdh.encrypt({
|
|
358
|
-
message: 'context-bound message',
|
|
359
|
-
secret: sharedKey,
|
|
360
|
-
opts: { additionalData: 'channel-123' },
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Decrypt must provide the SAME AAD
|
|
364
|
-
const decrypted = await ecdh.decrypt({
|
|
365
|
-
message: encrypted,
|
|
366
|
-
secret: sharedKey,
|
|
367
|
-
opts: { additionalData: 'channel-123' },
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// Decrypt with wrong/missing AAD throws
|
|
371
|
-
await ecdh.decrypt({ message: encrypted, secret: sharedKey });
|
|
372
|
-
// => throws (AAD mismatch)
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
#### Encrypted Payload Format
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
interface IECDHEncryptedPayload {
|
|
379
|
-
iv: string; // base64 encoded 12-byte IV
|
|
380
|
-
ct: string; // base64 encoded ciphertext + GCM auth tag (128-bit)
|
|
381
|
-
}
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
#### Security Properties
|
|
385
|
-
|
|
386
|
-
| Property | Guarantee |
|
|
387
|
-
|----------|-----------|
|
|
388
|
-
| **Confidentiality** | AES-256-GCM encryption |
|
|
389
|
-
| **Integrity** | GCM authentication tag -- tampered ciphertext is detected |
|
|
390
|
-
| **Forward secrecy** | Ephemeral key pairs -- compromising one session doesn't compromise others |
|
|
391
|
-
| **Key isolation** | HKDF info parameter separates key derivation contexts |
|
|
392
|
-
| **Context binding** | AAD (`additionalData`) prevents cross-context replay |
|
|
393
|
-
|
|
394
|
-
### Hashing
|
|
395
|
-
|
|
396
|
-
Standalone `hash` utility function for creating hashes (e.g., for data integrity checks or HMAC signatures).
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
import { hash } from '@venizia/ignis-helpers';
|
|
400
|
-
|
|
401
|
-
// MD5 Hash
|
|
402
|
-
const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
|
|
403
|
-
|
|
404
|
-
// SHA256 HMAC (secret is required for SHA256)
|
|
405
|
-
const sha256Hash = hash('some text', {
|
|
406
|
-
algorithm: 'SHA256',
|
|
407
|
-
secret: 'a-secret-key',
|
|
408
|
-
outputType: 'hex',
|
|
409
|
-
});
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
> [!WARNING]
|
|
413
|
-
> `SHA256` mode uses HMAC and **requires** the `secret` parameter. If `secret` is omitted, the function returns the original text unchanged (no hash is computed). `MD5` mode does not use a secret.
|
|
414
|
-
|
|
415
|
-
#### `hash` Function Signature
|
|
416
|
-
|
|
417
|
-
```typescript
|
|
418
|
-
function hash(
|
|
419
|
-
text: string,
|
|
420
|
-
options: {
|
|
421
|
-
algorithm: 'SHA256' | 'MD5';
|
|
422
|
-
secret?: string;
|
|
423
|
-
outputType: C.BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url'
|
|
424
|
-
},
|
|
425
|
-
): string;
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
| Option | Type | Required | Description |
|
|
429
|
-
|--------|------|----------|-------------|
|
|
430
|
-
| `algorithm` | `'SHA256' \| 'MD5'` | Yes | Hashing algorithm to use |
|
|
431
|
-
| `secret` | `string` | Only for SHA256 | HMAC secret key. Required for SHA256, ignored for MD5. |
|
|
432
|
-
| `outputType` | `'hex' \| 'base64' \| 'base64url'` | Yes | Output encoding of the hash digest |
|
|
433
|
-
|
|
434
|
-
## API Summary
|
|
435
|
-
|
|
436
|
-
| Method | Class | Returns | Description |
|
|
437
|
-
|--------|-------|---------|-------------|
|
|
438
|
-
| `AES.withAlgorithm(algorithm)` | AES | `AES` | Create AES instance with CBC or GCM mode |
|
|
439
|
-
| `encrypt(opts)` | AES | `string` | Encrypt a string message |
|
|
440
|
-
| `decrypt(opts)` | AES | `string` | Decrypt a ciphertext string |
|
|
441
|
-
| `encryptFile(opts)` | AES | `string` | Encrypt file contents to string |
|
|
442
|
-
| `decryptFile(opts)` | AES | `string` | Decrypt file contents to string |
|
|
443
|
-
| `RSA.withAlgorithm()` | RSA | `RSA` | Create RSA instance |
|
|
444
|
-
| `generateDERKeyPair(opts?)` | RSA | `{ publicKey: Buffer, privateKey: Buffer }` | Generate DER-format key pair |
|
|
445
|
-
| `encrypt(opts)` | RSA | `string` | Encrypt with public key |
|
|
446
|
-
| `decrypt(opts)` | RSA | `string` | Decrypt with private key |
|
|
447
|
-
| `ECDH.withAlgorithm(opts?)` | ECDH | `ECDH` | Create ECDH instance with optional HKDF info |
|
|
448
|
-
| `generateKeyPair()` | ECDH | `Promise<{ keyPair: CryptoKeyPair, publicKeyB64: string }>` | Generate P-256 key pair |
|
|
449
|
-
| `importPublicKey(opts)` | ECDH | `Promise<CryptoKey>` | Import peer's base64 public key |
|
|
450
|
-
| `deriveAESKey(opts)` | ECDH | `Promise<{ key: CryptoKey, salt: string }>` | Derive AES-256-GCM key via HKDF |
|
|
451
|
-
| `encrypt(opts)` | ECDH | `Promise<IECDHEncryptedPayload>` | Encrypt with derived AES key |
|
|
452
|
-
| `decrypt(opts)` | ECDH | `Promise<string>` | Decrypt with derived AES key |
|
|
453
|
-
| `hash(text, options)` | _(function)_ | `string` | MD5 or SHA256 HMAC hash |
|
|
454
|
-
|
|
455
|
-
## Troubleshooting
|
|
456
|
-
|
|
457
|
-
### "[validateAlgorithmName] Invalid algorithm name | algorithm: undefined"
|
|
458
|
-
|
|
459
|
-
**Cause:** An empty or undefined `algorithm` string was passed to the constructor (or `withAlgorithm()`).
|
|
460
|
-
|
|
461
|
-
**Fix:** Provide a valid algorithm name:
|
|
82
|
+
const rsa = RSA.withAlgorithm();
|
|
83
|
+
const { publicKey, privateKey } = rsa.generateDERKeyPair(); // default 2048-bit modulus
|
|
462
84
|
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
const ecdh = ECDH.withAlgorithm(); // No parameter needed
|
|
85
|
+
const encrypted = rsa.encrypt({ message: 'secret data', secret: publicKey.toString('base64') });
|
|
86
|
+
const decrypted = rsa.decrypt({ message: encrypted, secret: privateKey.toString('base64') });
|
|
87
|
+
// => 'secret data'
|
|
467
88
|
```
|
|
468
89
|
|
|
469
|
-
###
|
|
470
|
-
|
|
471
|
-
**Cause:** A value passed to an ECDH method (public key, salt, or encrypted payload) is not valid base64. The string must have a length divisible by 4 and contain only characters `A-Za-z0-9+/=`.
|
|
90
|
+
### Fail soft instead of throwing
|
|
472
91
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
### "Unsupported state or unable to authenticate data"
|
|
476
|
-
|
|
477
|
-
**Cause:** The ciphertext or auth tag was modified in transit, or you are decrypting GCM ciphertext with a CBC instance (or vice versa). CBC and GCM produce incompatible ciphertext formats.
|
|
478
|
-
|
|
479
|
-
**Fix:** Ensure the same algorithm mode is used for both encrypt and decrypt:
|
|
92
|
+
Pass `opts: { doThrow: false }` to get the original message back on error instead of an exception. Use this when a decryption failure should be a fallback path, not a crash.
|
|
480
93
|
|
|
481
94
|
```typescript
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const encrypted = aes.encrypt({ message, secret });
|
|
485
|
-
const decrypted = aes.decrypt({ message: encrypted, secret }); // same instance or same mode
|
|
95
|
+
const result = rsa.encrypt({ message: 'test', secret: 'invalid-key', opts: { doThrow: false } });
|
|
96
|
+
// result === 'test' (original message, no throw)
|
|
486
97
|
```
|
|
487
98
|
|
|
488
|
-
###
|
|
99
|
+
### Derive a shared session key with ECDH
|
|
489
100
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
**Fix:** The initiator calls `deriveAESKey` without `salt` (generates one), then sends the returned `salt` string to the responder. The responder passes that `salt` into their `deriveAESKey` call.
|
|
101
|
+
`ECDH` is async (Web Crypto) and needs a `deriveAESKey()` step before either side can encrypt. See the [Full reference](/extensions/helpers/crypto/reference#ecdh-key-exchange) for the complete key-exchange flow, salt handling, and additional authenticated data (AAD).
|
|
493
102
|
|
|
494
103
|
```typescript
|
|
495
|
-
|
|
496
|
-
const { key: aliceKey, salt } = await ecdh.deriveAESKey({
|
|
497
|
-
privateKey: alice.keyPair.privateKey,
|
|
498
|
-
peerPublicKey: bobPub,
|
|
499
|
-
});
|
|
500
|
-
// Send `salt` to responder
|
|
501
|
-
|
|
502
|
-
// Responder
|
|
503
|
-
const { key: bobKey } = await ecdh.deriveAESKey({
|
|
504
|
-
privateKey: bob.keyPair.privateKey,
|
|
505
|
-
peerPublicKey: alicePub,
|
|
506
|
-
salt, // <-- use initiator's salt
|
|
507
|
-
});
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
### SHA256 hash returns the original text instead of a hash
|
|
104
|
+
import { ECDH } from '@venizia/ignis-helpers';
|
|
511
105
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
const hashed = hash('text', {
|
|
518
|
-
algorithm: 'SHA256',
|
|
519
|
-
secret: 'my-hmac-key',
|
|
520
|
-
outputType: 'hex',
|
|
521
|
-
});
|
|
106
|
+
const ecdh = ECDH.withAlgorithm();
|
|
107
|
+
const alice = await ecdh.generateKeyPair();
|
|
108
|
+
const bob = await ecdh.generateKeyPair();
|
|
522
109
|
```
|
|
523
110
|
|
|
524
|
-
## See
|
|
525
|
-
|
|
526
|
-
- **Related Concepts:**
|
|
527
|
-
- [Services](/guides/core-concepts/services) -- Password hashing in user services
|
|
111
|
+
## See also
|
|
528
112
|
|
|
529
|
-
-
|
|
530
|
-
|
|
113
|
+
- [Full reference](/extensions/helpers/crypto/reference) - every option, the ECDH flow, `hash()`, and troubleshooting
|
|
114
|
+
- [Crypto Utility](/references/utilities/crypto) - the standalone `hash()` function for MD5/HMAC-SHA256
|
|
115
|
+
- [Authentication Component](/extensions/components/authentication/) - JWT and password verification
|
|
116
|
+
- [Helpers Overview](/extensions/helpers/) - all available helpers
|
|
117
|
+
- [Security Guidelines](/best-practices/security-guidelines) - cryptographic best practices
|
|
531
118
|
|
|
532
|
-
|
|
533
|
-
- [Crypto Utility](/references/utilities/crypto) -- Pure crypto utilities
|
|
534
|
-
- [Authentication Component](/extensions/components/authentication/) -- JWT and password verification
|
|
119
|
+
**Files:**
|
|
535
120
|
|
|
536
|
-
-
|
|
537
|
-
|
|
121
|
+
- [`packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts) - `AbstractCryptoAlgorithm`, `BaseCryptoAlgorithm`
|
|
122
|
+
- [`packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts) - `AES`
|
|
123
|
+
- [`packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts) - `RSA`
|
|
124
|
+
- [`packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts) - `ECDH`
|