@venizia/ignis-docs 0.2.0 → 0.2.1-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/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- 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 +26 -2
- 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 +6 -2
- 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 +182 -93
- 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 +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- 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/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- 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 +57 -218
- 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/package.json +8 -8
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Crypto - Full Reference
|
|
3
|
+
description: Complete reference for AES, RSA, ECDH, the shared algorithm base classes, and every option
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Crypto - Full Reference
|
|
8
|
+
|
|
9
|
+
Exhaustive reference for `AES`, `RSA`, `ECDH`, the shared `AbstractCryptoAlgorithm`/`BaseCryptoAlgorithm` base classes, and the standalone `hash()` utility. For a readable introduction and the most common tasks, start with the [Crypto overview](/extensions/helpers/crypto/).
|
|
10
|
+
|
|
11
|
+
**Files:**
|
|
12
|
+
|
|
13
|
+
- [`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`
|
|
14
|
+
- [`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`, `AESAlgorithmType`
|
|
15
|
+
- [`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`, `RSAAlgorithmType`
|
|
16
|
+
- [`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`, `ECDHAlgorithmType`, `IECDHEncryptedPayload`, `IECDHExtraOptions`
|
|
17
|
+
- [`packages/helpers/src/modules/crypto/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/common/types.ts) - `ICryptoAlgorithm`
|
|
18
|
+
- [`packages/helpers/src/modules/crypto/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/common/constants.ts) - `DEFAULT_CIPHER_BITS`, `DEFAULT_PAD_END`
|
|
19
|
+
- [`packages/helpers/src/utilities/crypto.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/crypto.utility.ts) - `hash()`
|
|
20
|
+
|
|
21
|
+
## Quick Reference
|
|
22
|
+
|
|
23
|
+
| Class | Extends | Secret type | Async | Runtime API |
|
|
24
|
+
|-------|---------|--------------|-------|-------------|
|
|
25
|
+
| `AES` | `BaseCryptoAlgorithm` | `string` | No | Node `node:crypto` |
|
|
26
|
+
| `RSA` | `BaseCryptoAlgorithm` | `string` (base64 DER key) | No | Node `node:crypto` |
|
|
27
|
+
| `ECDH` | `AbstractCryptoAlgorithm` | `CryptoKey` | Yes | Web Crypto (`crypto.subtle`) |
|
|
28
|
+
| `hash()` | _(standalone function)_ | `string` (HMAC secret, SHA256 only) | No | Node `node:crypto` |
|
|
29
|
+
|
|
30
|
+
### Import paths
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// Algorithm classes
|
|
34
|
+
import { AES, RSA, ECDH } from '@venizia/ignis-helpers';
|
|
35
|
+
|
|
36
|
+
// Hash utility function
|
|
37
|
+
import { hash } from '@venizia/ignis-helpers';
|
|
38
|
+
|
|
39
|
+
// Types
|
|
40
|
+
import type {
|
|
41
|
+
AESAlgorithmType,
|
|
42
|
+
RSAAlgorithmType,
|
|
43
|
+
ECDHAlgorithmType,
|
|
44
|
+
IECDHEncryptedPayload,
|
|
45
|
+
IECDHExtraOptions,
|
|
46
|
+
ICryptoAlgorithm,
|
|
47
|
+
} from '@venizia/ignis-helpers';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
All of the above resolve through the root `@venizia/ignis-helpers` barrel, which re-exports `./modules` (and therefore `./modules/crypto`) and `./utilities` in full.
|
|
51
|
+
|
|
52
|
+
## Type Hierarchy
|
|
53
|
+
|
|
54
|
+
`Source ->` [`base.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
ICryptoAlgorithm (interface)
|
|
58
|
+
└── AbstractCryptoAlgorithm (extends BaseHelper)
|
|
59
|
+
├── BaseCryptoAlgorithm (adds normalizeSecretKey, getAlgorithmKeySize)
|
|
60
|
+
│ ├── AES
|
|
61
|
+
│ └── RSA
|
|
62
|
+
└── ECDH (uses CryptoKey objects, not string secrets)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`ICryptoAlgorithm` carries 7 generic type parameters - the algorithm name literal, encrypt/decrypt input and output types, the secret key type, and an extra-options type:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface ICryptoAlgorithm<
|
|
69
|
+
AlgorithmNameType extends string,
|
|
70
|
+
EncryptInputType = unknown,
|
|
71
|
+
DecryptInputType = unknown,
|
|
72
|
+
SecretKeyType = unknown,
|
|
73
|
+
EncryptReturnType = unknown,
|
|
74
|
+
DecryptReturnType = unknown,
|
|
75
|
+
ExtraOptions = unknown,
|
|
76
|
+
> {
|
|
77
|
+
algorithm: AlgorithmNameType;
|
|
78
|
+
encrypt(opts: { message: EncryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): EncryptReturnType;
|
|
79
|
+
decrypt(opts: { message: DecryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): DecryptReturnType;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`AbstractCryptoAlgorithm` extends `BaseHelper` and declares `encrypt`/`decrypt` as abstract; it adds no behavior of its own. `BaseCryptoAlgorithm` is the concrete base for string-secret algorithms:
|
|
84
|
+
|
|
85
|
+
| Member | Signature | Description |
|
|
86
|
+
|--------|-----------|--------------|
|
|
87
|
+
| constructor | `(opts: { scope: string; algorithm: AlgorithmType })` | Sets `this.algorithm`, calls `validateAlgorithmName` |
|
|
88
|
+
| `validateAlgorithmName` | `(opts: { algorithm: AlgorithmType }) => void` | Throws if `algorithm` is empty/falsy |
|
|
89
|
+
| `normalizeSecretKey` | `(opts: { secret: string; length: number; padEnd?: string }) => string` | Truncates to `length` or right-pads with `padEnd` (default `'0'` - the digit character, since `DEFAULT_PAD_END` is `(0x00).toString()`, not a null byte) |
|
|
90
|
+
| `getAlgorithmKeySize` | `() => number` | Parses the bit size out of `this.algorithm` (e.g. `256` from `'aes-256-gcm'`), divides by 8 for byte length |
|
|
91
|
+
|
|
92
|
+
`ECDH` extends `AbstractCryptoAlgorithm` directly - it does not inherit `normalizeSecretKey` or `getAlgorithmKeySize`, since its secrets are `CryptoKey` objects, not strings.
|
|
93
|
+
|
|
94
|
+
## AES
|
|
95
|
+
|
|
96
|
+
`Source ->` [`aes.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts)
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
type AESAlgorithmType = 'aes-256-cbc' | 'aes-256-gcm';
|
|
100
|
+
|
|
101
|
+
const aes = AES.withAlgorithm('aes-256-gcm'); // or 'aes-256-cbc'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Mode | Authenticated | Notes |
|
|
105
|
+
|------|----------------|-------|
|
|
106
|
+
| `aes-256-cbc` | No | Plain block cipher, no tamper detection |
|
|
107
|
+
| `aes-256-gcm` | Yes | Ciphertext includes a 16-byte GCM auth tag; tampering throws on decrypt |
|
|
108
|
+
|
|
109
|
+
### `encrypt`
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
encrypt(opts: { message: string; secret: string; opts?: IAESExtraOptions }): string
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Option (`opts.opts`) | Type | Default | Description |
|
|
116
|
+
|-----------------------|------|---------|-------------|
|
|
117
|
+
| `iv` | `Buffer` | `crypto.randomBytes(16)` | Initialization vector |
|
|
118
|
+
| `inputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of `message` |
|
|
119
|
+
| `outputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of the returned ciphertext |
|
|
120
|
+
| `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
|
|
121
|
+
|
|
122
|
+
The secret is normalized via `normalizeSecretKey` to the algorithm's key size (32 bytes for both modes) before being used as the cipher key. The output layout is `IV [+ GCM auth tag] + ciphertext`, concatenated and encoded with `outputEncoding`.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import C from 'node:crypto';
|
|
126
|
+
|
|
127
|
+
const encrypted = aes.encrypt({
|
|
128
|
+
message: 'hello',
|
|
129
|
+
secret: 'my-secret',
|
|
130
|
+
opts: {
|
|
131
|
+
iv: C.randomBytes(16),
|
|
132
|
+
inputEncoding: 'utf-8',
|
|
133
|
+
outputEncoding: 'hex',
|
|
134
|
+
doThrow: false,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `decrypt`
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
decrypt(opts: { message: string; secret: string; opts?: IAESExtraOptions }): string
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Option (`opts.opts`) | Type | Default | Description |
|
|
146
|
+
|-----------------------|------|---------|-------------|
|
|
147
|
+
| `iv` | `Buffer` | Extracted from the first 16 bytes of the decoded ciphertext | Initialization vector |
|
|
148
|
+
| `inputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of `message` |
|
|
149
|
+
| `outputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of the returned plaintext |
|
|
150
|
+
| `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
|
|
151
|
+
|
|
152
|
+
For `aes-256-gcm`, the next 16 bytes after the IV are read as the auth tag and passed to `setAuthTag` before the remaining bytes are treated as ciphertext.
|
|
153
|
+
|
|
154
|
+
> [!WARNING]
|
|
155
|
+
> Decrypting `aes-256-gcm` ciphertext with an `aes-256-cbc` instance (or vice versa) throws `Unsupported state or unable to authenticate data` - the two modes produce incompatible byte layouts. Always encrypt and decrypt with the same algorithm mode.
|
|
156
|
+
|
|
157
|
+
### `encryptFile` / `decryptFile`
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
encryptFile(opts: { absolutePath: string; secret: string }): string
|
|
161
|
+
decryptFile(opts: { absolutePath: string; secret: string }): string
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Both read the file synchronously via `fs.readFileSync`, decode it as UTF-8, then call `encrypt`/`decrypt` on the string content using default extra-options. If `absolutePath` is empty or falsy, both return `''` without touching the filesystem.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const encrypted = aes.encryptFile({ absolutePath: '/path/to/config.json', secret: 'my-secret' });
|
|
168
|
+
const decrypted = aes.decryptFile({ absolutePath: '/path/to/config.json.enc', secret: 'my-secret' });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## RSA
|
|
172
|
+
|
|
173
|
+
`Source ->` [`rsa.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts)
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
type RSAAlgorithmType = 'rsa';
|
|
177
|
+
|
|
178
|
+
const rsa = RSA.withAlgorithm(); // no parameters - always algorithm 'rsa'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `generateDERKeyPair`
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
generateDERKeyPair(opts?: { modulus: number }): { publicKey: Buffer; privateKey: Buffer }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Option | Type | Default | Description |
|
|
188
|
+
|--------|------|---------|-------------|
|
|
189
|
+
| `modulus` | `number` | `2048` | RSA modulus length in bits, passed to `crypto.generateKeyPairSync` |
|
|
190
|
+
|
|
191
|
+
`publicKey` is exported as `{ type: 'spki', format: 'der' }`; `privateKey` as `{ type: 'pkcs8', format: 'der' }`. Both are raw `Buffer`s - base64-encode them (`.toString('base64')`) to pass as the `secret` string to `encrypt`/`decrypt`.
|
|
192
|
+
|
|
193
|
+
### `encrypt`
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
encrypt(opts: { message: string; secret: string; opts?: IRSAExtraOptions }): string
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`secret` is the base64-encoded public key (SPKI/DER). Internally builds a public key via `crypto.createPublicKey({ format: 'der', type: 'spki' })` and calls `crypto.publicEncrypt`.
|
|
200
|
+
|
|
201
|
+
| Option (`opts.opts`) | Type | Default | Description |
|
|
202
|
+
|-----------------------|------|---------|-------------|
|
|
203
|
+
| `inputEncoding.key` | `crypto.Encoding` | `'base64'` | Encoding of the `secret` key buffer |
|
|
204
|
+
| `inputEncoding.message` | `crypto.Encoding` | `'utf-8'` | Encoding of `message` |
|
|
205
|
+
| `outputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of the returned ciphertext |
|
|
206
|
+
| `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
|
|
207
|
+
|
|
208
|
+
### `decrypt`
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
decrypt(opts: { message: string; secret: string; opts?: IRSAExtraOptions }): string
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`secret` is the base64-encoded private key (PKCS8/DER). Internally builds a private key via `crypto.createPrivateKey({ format: 'der', type: 'pkcs8' })` and calls `crypto.privateDecrypt`.
|
|
215
|
+
|
|
216
|
+
| Option (`opts.opts`) | Type | Default | Description |
|
|
217
|
+
|-----------------------|------|---------|-------------|
|
|
218
|
+
| `inputEncoding.key` | `crypto.Encoding` | `'base64'` | Encoding of the `secret` key buffer |
|
|
219
|
+
| `inputEncoding.message` | `crypto.Encoding` | `'base64'` | Encoding of `message` |
|
|
220
|
+
| `outputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of the returned plaintext |
|
|
221
|
+
| `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const rsa = RSA.withAlgorithm();
|
|
225
|
+
const { publicKey, privateKey } = rsa.generateDERKeyPair({ modulus: 4096 });
|
|
226
|
+
|
|
227
|
+
const encrypted = rsa.encrypt({
|
|
228
|
+
message: 'hello',
|
|
229
|
+
secret: publicKey.toString('base64'),
|
|
230
|
+
opts: { outputEncoding: 'hex', doThrow: false },
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const decrypted = rsa.decrypt({
|
|
234
|
+
message: encrypted,
|
|
235
|
+
secret: privateKey.toString('base64'),
|
|
236
|
+
opts: { inputEncoding: { key: 'base64', message: 'hex' } },
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## ECDH Key Exchange
|
|
241
|
+
|
|
242
|
+
`Source ->` [`ecdh.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts)
|
|
243
|
+
|
|
244
|
+
`ECDH` implements ECDH P-256 key exchange with HKDF-derived AES-256-GCM session encryption, entirely on the Web Crypto API (`crypto.subtle`). Every method is `async`.
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
const ecdh = ECDH.withAlgorithm(); // curve is always 'ecdh-p256'
|
|
248
|
+
const ecdhCustom = ECDH.withAlgorithm({ hkdfInfo: 'my-app-session-keys' });
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
| Constructor option | Type | Default | Description |
|
|
252
|
+
|---------------------|------|---------|-------------|
|
|
253
|
+
| `algorithm` | `'ecdh-p256'` | `'ecdh-p256'` | Accepted but not branched on - the curve is always P-256 |
|
|
254
|
+
| `hkdfInfo` | `string` | `'ignis-ecdh-p256-aes-256-gcm-v1'` | HKDF info string, UTF-8 encoded, used to isolate key derivation between application contexts |
|
|
255
|
+
|
|
256
|
+
Different `hkdfInfo` values produce **incompatible** derived keys from the same ECDH shared secret.
|
|
257
|
+
|
|
258
|
+
### Key generation and import
|
|
259
|
+
|
|
260
|
+
| Method | Signature | Description |
|
|
261
|
+
|--------|-----------|--------------|
|
|
262
|
+
| `generateKeyPair` | `() => Promise<{ keyPair: CryptoKeyPair; publicKeyB64: string }>` | `deriveBits`-only key pair (`extractable: false`); `publicKeyB64` is the raw exported public key, base64-encoded (65 bytes for P-256) |
|
|
263
|
+
| `importPublicKey` | `(opts: { rawKeyB64: string }) => Promise<CryptoKey>` | Imports a peer's raw base64 public key |
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
const { keyPair, publicKeyB64 } = await ecdh.generateKeyPair();
|
|
267
|
+
const peerKey = await ecdh.importPublicKey({ rawKeyB64: peerPublicKeyB64 });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### `deriveAESKey`
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
deriveAESKey(opts: {
|
|
274
|
+
privateKey: CryptoKey;
|
|
275
|
+
peerPublicKey: CryptoKey;
|
|
276
|
+
salt?: string;
|
|
277
|
+
}): Promise<{ key: CryptoKey; salt: string }>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Derives shared bits via ECDH (`deriveBits`, 256 bits), imports them as an HKDF key, then derives a non-extractable AES-256-GCM `CryptoKey` via HKDF-SHA256 using `salt` and the instance's `hkdfInfo`.
|
|
281
|
+
|
|
282
|
+
| Option | Type | Default | Description |
|
|
283
|
+
|--------|------|---------|-------------|
|
|
284
|
+
| `privateKey` | `CryptoKey` | - | Caller's ECDH private key from `generateKeyPair()` |
|
|
285
|
+
| `peerPublicKey` | `CryptoKey` | - | Peer's public key from `importPublicKey()` |
|
|
286
|
+
| `salt` | `string` | A random 32-byte salt is generated | Base64-encoded HKDF salt. Omit to generate a new random one |
|
|
287
|
+
|
|
288
|
+
> [!IMPORTANT]
|
|
289
|
+
> Both parties must use the **same salt** to derive matching keys. The initiator omits `salt` (a random one is generated and returned); the responder must pass that returned `salt` back into their own `deriveAESKey` call. If both sides generate their own salt, they derive different, non-matching keys.
|
|
290
|
+
|
|
291
|
+
### `encrypt` / `decrypt`
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
encrypt(opts: { message: string; secret: CryptoKey; opts?: IECDHExtraOptions }): Promise<IECDHEncryptedPayload>
|
|
295
|
+
decrypt(opts: { message: IECDHEncryptedPayload; secret: CryptoKey; opts?: IECDHExtraOptions }): Promise<string>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`secret` is the `CryptoKey` returned by `deriveAESKey`. Uses AES-GCM with a random 12-byte IV per call and a 128-bit auth tag.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
interface IECDHEncryptedPayload {
|
|
302
|
+
iv: string; // base64 encoded 12-byte IV
|
|
303
|
+
ct: string; // base64 encoded ciphertext + 128-bit GCM auth tag
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
interface IECDHExtraOptions {
|
|
307
|
+
additionalData?: string;
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`opts.additionalData` (AAD) is authenticated but not encrypted - it binds the ciphertext to a context (channel ID, session ID) so the ciphertext cannot be replayed in a different context. Decrypt must supply the exact same `additionalData`; a mismatch (including an omitted value where one was used to encrypt) throws.
|
|
312
|
+
|
|
313
|
+
### Complete flow
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const ecdh = ECDH.withAlgorithm();
|
|
317
|
+
|
|
318
|
+
// 1. Both parties generate key pairs
|
|
319
|
+
const alice = await ecdh.generateKeyPair();
|
|
320
|
+
const bob = await ecdh.generateKeyPair();
|
|
321
|
+
|
|
322
|
+
// 2. Exchange public keys (safe to share over any channel)
|
|
323
|
+
const alicePubForBob = await ecdh.importPublicKey({ rawKeyB64: alice.publicKeyB64 });
|
|
324
|
+
const bobPubForAlice = await ecdh.importPublicKey({ rawKeyB64: bob.publicKeyB64 });
|
|
325
|
+
|
|
326
|
+
// 3. Initiator derives the AES key (generates a random salt)
|
|
327
|
+
const { key: aliceKey, salt } = await ecdh.deriveAESKey({
|
|
328
|
+
privateKey: alice.keyPair.privateKey,
|
|
329
|
+
peerPublicKey: bobPubForAlice,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// 4. Responder derives the SAME AES key using the initiator's salt
|
|
333
|
+
const { key: bobKey } = await ecdh.deriveAESKey({
|
|
334
|
+
privateKey: bob.keyPair.privateKey,
|
|
335
|
+
peerPublicKey: alicePubForBob,
|
|
336
|
+
salt,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// 5. Alice encrypts, Bob decrypts (or vice versa)
|
|
340
|
+
const encrypted = await ecdh.encrypt({ message: 'Hello Bob!', secret: aliceKey });
|
|
341
|
+
const decrypted = await ecdh.decrypt({ message: encrypted, secret: bobKey });
|
|
342
|
+
// => 'Hello Bob!'
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Security properties
|
|
346
|
+
|
|
347
|
+
| Property | Guarantee |
|
|
348
|
+
|----------|-----------|
|
|
349
|
+
| Confidentiality | AES-256-GCM encryption |
|
|
350
|
+
| Integrity | GCM authentication tag - tampered ciphertext is detected on decrypt |
|
|
351
|
+
| Forward secrecy | Ephemeral, non-extractable key pairs - compromising one session does not compromise others |
|
|
352
|
+
| Key isolation | `hkdfInfo` separates key derivation across application contexts |
|
|
353
|
+
| Context binding | `additionalData` (AAD) prevents cross-context replay |
|
|
354
|
+
|
|
355
|
+
## Hashing
|
|
356
|
+
|
|
357
|
+
`Source ->` [`crypto.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/crypto.utility.ts)
|
|
358
|
+
|
|
359
|
+
`hash()` is a standalone function - not a class, not part of the `AES`/`RSA`/`ECDH` hierarchy - exported from `packages/helpers/src/utilities`, not `modules/crypto`. It is documented in full on the [Crypto Utility reference](/references/utilities/crypto).
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
function hash(
|
|
363
|
+
text: string,
|
|
364
|
+
options: {
|
|
365
|
+
algorithm: 'SHA256' | 'MD5';
|
|
366
|
+
secret?: string;
|
|
367
|
+
outputType: C.BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url' | 'latin1'
|
|
368
|
+
},
|
|
369
|
+
): string;
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
| Option | Type | Required | Description |
|
|
373
|
+
|--------|------|----------|-------------|
|
|
374
|
+
| `algorithm` | `'SHA256' \| 'MD5'` | Yes | Hashing algorithm |
|
|
375
|
+
| `secret` | `string` | Only for `SHA256` | HMAC secret key; ignored for `MD5` |
|
|
376
|
+
| `outputType` | `'hex' \| 'base64' \| 'base64url' \| 'latin1'` | Yes | Digest output encoding |
|
|
377
|
+
|
|
378
|
+
> [!WARNING]
|
|
379
|
+
> `SHA256` uses `createHmac` and **requires** `secret`. If `secret` is `undefined`, the function short-circuits and returns `text` unchanged - no hash is computed, no error is thrown. `MD5` never needs a secret and always hashes.
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import { hash } from '@venizia/ignis-helpers';
|
|
383
|
+
|
|
384
|
+
const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
|
|
385
|
+
const sha256Hmac = hash('some text', { algorithm: 'SHA256', secret: 'a-secret-key', outputType: 'hex' });
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## API Summary
|
|
389
|
+
|
|
390
|
+
| Method | Class | Returns | Description |
|
|
391
|
+
|--------|-------|---------|-------------|
|
|
392
|
+
| `AES.withAlgorithm(algorithm)` | `AES` | `AES` | Create an AES instance with CBC or GCM mode |
|
|
393
|
+
| `encrypt(opts)` | `AES` | `string` | Encrypt a string message |
|
|
394
|
+
| `decrypt(opts)` | `AES` | `string` | Decrypt a ciphertext string |
|
|
395
|
+
| `encryptFile(opts)` | `AES` | `string` | Encrypt file contents to a string |
|
|
396
|
+
| `decryptFile(opts)` | `AES` | `string` | Decrypt file contents to a string |
|
|
397
|
+
| `RSA.withAlgorithm()` | `RSA` | `RSA` | Create an RSA instance |
|
|
398
|
+
| `generateDERKeyPair(opts?)` | `RSA` | `{ publicKey: Buffer; privateKey: Buffer }` | Generate a DER-format key pair |
|
|
399
|
+
| `encrypt(opts)` | `RSA` | `string` | Encrypt with a public key |
|
|
400
|
+
| `decrypt(opts)` | `RSA` | `string` | Decrypt with a private key |
|
|
401
|
+
| `ECDH.withAlgorithm(opts?)` | `ECDH` | `ECDH` | Create an ECDH instance with optional `hkdfInfo` |
|
|
402
|
+
| `generateKeyPair()` | `ECDH` | `Promise<{ keyPair: CryptoKeyPair; publicKeyB64: string }>` | Generate a P-256 key pair |
|
|
403
|
+
| `importPublicKey(opts)` | `ECDH` | `Promise<CryptoKey>` | Import a peer's base64 public key |
|
|
404
|
+
| `deriveAESKey(opts)` | `ECDH` | `Promise<{ key: CryptoKey; salt: string }>` | Derive an AES-256-GCM key via HKDF |
|
|
405
|
+
| `encrypt(opts)` | `ECDH` | `Promise<IECDHEncryptedPayload>` | Encrypt with a derived AES key |
|
|
406
|
+
| `decrypt(opts)` | `ECDH` | `Promise<string>` | Decrypt with a derived AES key |
|
|
407
|
+
| `hash(text, options)` | _(function)_ | `string` | MD5 digest or HMAC-SHA256 |
|
|
408
|
+
|
|
409
|
+
## Troubleshooting
|
|
410
|
+
|
|
411
|
+
### "[validateAlgorithmName] Invalid algorithm name | algorithm: undefined"
|
|
412
|
+
|
|
413
|
+
**Cause:** An empty or undefined `algorithm` was passed to the constructor (or `withAlgorithm()`).
|
|
414
|
+
|
|
415
|
+
**Fix:**
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
const aes = AES.withAlgorithm('aes-256-gcm'); // not undefined or empty
|
|
419
|
+
const rsa = RSA.withAlgorithm(); // no parameter needed
|
|
420
|
+
const ecdh = ECDH.withAlgorithm(); // no parameter needed
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### "[ECDH.fromBase64] Invalid base64 input"
|
|
424
|
+
|
|
425
|
+
**Cause:** A value passed to an ECDH method (public key, salt, IV, or ciphertext) is not valid base64 - length not divisible by 4, or characters outside `A-Za-z0-9+/=`.
|
|
426
|
+
|
|
427
|
+
**Fix:** Pass base64 strings through exactly as produced by the methods that generated them (`publicKeyB64`, `salt`, `iv`, `ct`) - do not trim, re-encode, or modify them.
|
|
428
|
+
|
|
429
|
+
### "Unsupported state or unable to authenticate data"
|
|
430
|
+
|
|
431
|
+
**Cause:** The ciphertext or auth tag was modified in transit, or you decrypted `aes-256-gcm` ciphertext with a `aes-256-cbc` instance (or vice versa) - the two modes produce incompatible byte layouts.
|
|
432
|
+
|
|
433
|
+
**Fix:** Use the same algorithm mode for both encrypt and decrypt.
|
|
434
|
+
|
|
435
|
+
### ECDH decrypt throws even though both sides imported each other's public keys
|
|
436
|
+
|
|
437
|
+
**Cause:** Each `deriveAESKey` call without a `salt` generates a new random 32-byte salt. If both sides generate their own, they derive different AES keys.
|
|
438
|
+
|
|
439
|
+
**Fix:** The initiator calls `deriveAESKey` without `salt` and sends the returned `salt` to the responder; the responder passes that exact `salt` into their own `deriveAESKey` call.
|
|
440
|
+
|
|
441
|
+
### SHA256 hash returns the original text instead of a hash
|
|
442
|
+
|
|
443
|
+
**Cause:** `hash()` with `algorithm: 'SHA256'` requires `secret`; when `secret` is `undefined` it short-circuits and returns `text` unchanged.
|
|
444
|
+
|
|
445
|
+
**Fix:**
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
const hashed = hash('text', { algorithm: 'SHA256', secret: 'my-hmac-key', outputType: 'hex' });
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## See also
|
|
452
|
+
|
|
453
|
+
- [Crypto overview](/extensions/helpers/crypto/) - introduction and the most common tasks
|
|
454
|
+
- [Crypto Utility](/references/utilities/crypto) - full `hash()` reference
|
|
455
|
+
- [Authentication Component](/extensions/components/authentication/) - JWT and password verification
|
|
456
|
+
- [Security Guidelines](/best-practices/security-guidelines) - cryptographic best practices
|